javascript
Spring框架中的设计模式(一)
設(shè)計模式有助于遵循良好的編程實踐。作為最流行的Web框架之一的Spring框架也使用其中的一些。
本文將介紹Spring Framework中使用的設(shè)計模式。這是5篇專題文章的第一部分。這次我們將發(fā)現(xiàn)Spring框架中使用的4種設(shè)計模式:解釋器,構(gòu)造器,工廠方法和抽象工廠。每部分將首先解釋給定模式的原理。緊接著,將會使用Spring的一個例子來加深理解。
解釋器模式
在現(xiàn)實世界中,我們?nèi)祟愋枰忉屖謩荨K麄兛梢詫ξ幕胁煌暮x。這是我們的解釋,給他們一個意義。在編程中,我們還需要分析一件事情,并決定它是什么意思。我們可以用解釋設(shè)計模式來做。
此模式基于表達式和評估器部分。第一個代表一個要分析的事情。這個分析是由評價者來做出的,它們知道構(gòu)成表達的人物的意義。不必要的操作是在一個上下文中進行的。
Spring主要以Spring Expression Language(SpEL)為例。這里快速提個醒,SpEL是一種由Spring的org.springframework.expression.ExpressionParser實現(xiàn)分析和執(zhí)行的語言。這些實現(xiàn)使用作為字符串給出的Spel表達式,并將它們轉(zhuǎn)換為org.springframework.expression.Expression的實例。上下文組件由org.springframework.expression.EvaluationContext實現(xiàn)表示,例如:StandardEvaluationContext。
舉個SpEL的一個例子:
Writer writer = new Writer(); writer.setName("Writer's name");StandardEvaluationContext modifierContext = new StandardEvaluationContext(subscriberContext);modifierContext.setVariable("name", "Overriden writer's name"); parser.parseExpression("name = #name").getValue(modifierContext);System.out.println("writer's name is : " + writer.getName());?
構(gòu)造模式
輸出應(yīng)打印“Overriden writer’s name”。如你所見,一個對象的屬性是通過一個表達式name = #name進行修改的,這個表達式只有在ExpressionParser才能理解,因為提供了context(前面的樣例中的modifierContext實例)。
構(gòu)造模式是屬于創(chuàng)建對象模式三劍客的第一種模式。該模式用于簡化復(fù)雜對象的構(gòu)造。要理解這個概念,想象一個說明程序員簡歷的對象。在這個對象中,我們想存儲個人信息(名字,地址等)以及技術(shù)信息(知識語言,已實現(xiàn)的項目等)。該對象的構(gòu)造可能如下所示:
// with constructor Programmer programmer = new Programmer("first name", "last name", "address Street 39", "ZIP code", "City", "Country", birthDateObject, new String[] {"Java", "PHP", "Perl", "SQL"}, new String[] {"CRM system", "CMS system for government"});// or with setters Programmer programmer = new Programmer(); programmer.setName("first name"); programmer.setLastName("last name");// ... multiple lines after programmer.setProjects(new String[] {"CRM system", "CMS system for government"});Builder允許我們通過使用將值傳遞給父類的內(nèi)部構(gòu)建器對象來清楚地分解對象構(gòu)造。所以對于我們這個程序員簡歷的對象的創(chuàng)建,構(gòu)建器可以看起來像:
public class BuilderTest {@Testpublic void test() {Programmer programmer = new Programmer.ProgrammerBuilder().setFirstName("F").setLastName("L").setCity("City").setZipCode("0000A").setAddress("Street 39").setLanguages(new String[] {"bash", "Perl"}).setProjects(new String[] {"Linux kernel"}).build();assertTrue("Programmer should be 'F L' but was '" + programmer + "'",programmer.toString().equals("F L"));} }class Programmer {private String firstName;private String lastName;private String address;private String zipCode;private String city;private String[] languages;private String[] projects;private Programmer(String fName, String lName, String addr, String zip, String city, String[] langs, String[] projects) {this.firstName = fName;this.lastName = lName;this.address = addr;this.zipCode = zip;this.city = city;this.languages = langs;this.projects = projects;}public static class ProgrammerBuilder {private String firstName;private String lastName;private String address;private String zipCode;private String city;private String[] languages;private String[] projects;public ProgrammerBuilder setFirstName(String firstName) {this.firstName = firstName;return this;}public ProgrammerBuilder setLastName(String lastName) {this.lastName = lastName;return this;}public ProgrammerBuilder setAddress(String address) {this.address = address;return this;}public ProgrammerBuilder setZipCode(String zipCode) {this.zipCode = zipCode;return this;}public ProgrammerBuilder setCity(String city) {this.city = city;return this;}public ProgrammerBuilder setLanguages(String[] languages) {this.languages = languages;return this;}public ProgrammerBuilder setProjects(String[] projects) {this.projects = projects;return this;}public Programmer build() {return new Programmer(firstName, lastName, address, zipCode, city, languages, projects);}}@Overridepublic String toString() {return this.firstName + " "+this.lastName;}}可以看出,構(gòu)建器后面隱藏了對象構(gòu)造的復(fù)雜性,內(nèi)部靜態(tài)類接受鏈接方法的調(diào)用。在Spring中,我們可以在org.springframework.beans.factory.support.BeanDefinitionBuilder類中檢索這個邏輯。這是一個允許我們以編程方式定義bean的類。我們可以在關(guān)于bean工廠后處理器的文章中看到它,BeanDefinitionBuilder包含幾個方法,它們?yōu)锳bstractBeanDefinition抽象類的相關(guān)實現(xiàn)設(shè)置值,比如作用域,工廠方法,屬性等。想看看它是如何工作的,請查看以下這些方法:
public class BeanDefinitionBuilder {/*** The {@code BeanDefinition} instance we are creating.*/private AbstractBeanDefinition beanDefinition;// ... some not important methods for this article// Some of building methods/*** Set the name of the parent definition of this bean definition.*/public BeanDefinitionBuilder setParentName(String parentName) {this.beanDefinition.setParentName(parentName);return this;}/*** Set the name of the factory method to use for this definition.*/public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {this.beanDefinition.setFactoryMethodName(factoryMethod);return this;}/*** Add an indexed constructor arg value. The current index is tracked internally* and all additions are at the present point.* @deprecated since Spring 2.5, in favor of {@link #addConstructorArgValue}*/@Deprecatedpublic BeanDefinitionBuilder addConstructorArg(Object value) {return addConstructorArgValue(value);}/*** Add an indexed constructor arg value. The current index is tracked internally* and all additions are at the present point.*/public BeanDefinitionBuilder addConstructorArgValue(Object value) {this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(this.constructorArgIndex++, value);return this;}/*** Add a reference to a named bean as a constructor arg.* @see #addConstructorArgValue(Object)*/public BeanDefinitionBuilder addConstructorArgReference(String beanName) {this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(this.constructorArgIndex++,new RuntimeBeanReference(beanName));return this;}/*** Add the supplied property value under the given name.*/public BeanDefinitionBuilder addPropertyValue(String name, Object value) {this.beanDefinition.getPropertyValues().add(name, value);return this;}/*** Add a reference to the specified bean name under the property specified.* @param name the name of the property to add the reference to* @param beanName the name of the bean being referenced*/public BeanDefinitionBuilder addPropertyReference(String name,String beanName) {this.beanDefinition.getPropertyValues().add(name, new RuntimeBeanReference(beanName));return this;}/*** Set the init method for this definition.*/public BeanDefinitionBuilder setInitMethodName(String methodName) {this.beanDefinition.setInitMethodName(methodName);return this;}// Methods that can be used to construct BeanDefinition/*** Return the current BeanDefinition object in its raw (unvalidated) form.* @see #getBeanDefinition()*/public AbstractBeanDefinition getRawBeanDefinition() {return this.beanDefinition;}/*** Validate and return the created BeanDefinition object.*/public AbstractBeanDefinition getBeanDefinition() {this.beanDefinition.validate();return this.beanDefinition;} }
工廠方法模式
創(chuàng)建對象模式三劍客的第二個成員是工廠方法模式。它完全適于使用動態(tài)環(huán)境作為Spring框架。實際上,這種模式允許通過公共靜態(tài)方法對象進行初始化,稱為工廠方法。在這個概念中,我們需要定義一個接口來創(chuàng)建對象。但是創(chuàng)建是由使用相關(guān)對象的類創(chuàng)建的。
但是在跳到Spring世界之前,讓我們用Java代碼做一個例子:
public class FactoryMethodTest {@Testpublic void test() {Meal fruit = Meal.valueOf("banana");Meal vegetable = Meal.valueOf("carrot");assertTrue("Banana should be a fruit but is " + fruit.getType(),fruit.getType().equals("fruit"));assertTrue("Carrot should be a vegetable but is " +vegetable.getType(), vegetable.getType().equals("vegetable"));} }class Meal {private String type;public Meal(String type) {this.type = type;}public String getType() {return this.type;}// Example of factory method - different object is created depending on current contextpublic static Meal valueOf(String ingredient) {if (ingredient.equals("banana")) {return new Meal("fruit");}return new Meal("vegetable");} }
在Spring中,我們可以通過指定的工廠方法創(chuàng)建bean。該方法與以前代碼示例中看到的valueOf方法完全相同。它是靜態(tài)的,可以采取沒有或多個參數(shù)。為了更好地了解案例,讓我們來看一下實例。首先搞定下配置:
現(xiàn)在請關(guān)注這個bean的初始化:
public class Welcomer {private String message;public Welcomer(String message) {this.message = message;}public static Welcomer createWelcomer(MessageLocator messagesLocator) {Calendar cal = Calendar.getInstance();String msgKey = "welcome.pm";if (cal.get(Calendar.AM_PM) == Calendar.AM) {msgKey = "welcome.am";}return new Welcomer(messagesLocator.getMessageByKey(msgKey));} }
當(dāng)Spring將構(gòu)造welcomerBean時,它不會通過傳統(tǒng)的構(gòu)造函數(shù),而是通過定義的靜態(tài)工廠方法createWelcomer來實現(xiàn)。還要注意,這個方法接受一些參數(shù)(MessageLocator bean的實例包含所有可用的消息) 標(biāo)簽,通常保留給傳統(tǒng)的構(gòu)造函數(shù)。
?
抽象工廠模式
最后一個,抽象的工廠設(shè)計模式,看起來類似于工廠方法。不同之處在于,我們可以將抽象工廠視為這個詞的工業(yè)意義上的工廠,即。作為提供所需對象的東西。工廠部件有:抽象工廠,抽象產(chǎn)品,產(chǎn)品和客戶。更準(zhǔn)確地說,抽象工廠定義了構(gòu)建對象的方法。抽象產(chǎn)品是這種結(jié)構(gòu)的結(jié)果。產(chǎn)品是具有同樣結(jié)構(gòu)的具體結(jié)果。客戶是要求創(chuàng)造產(chǎn)品來抽象工廠的人。
同樣的,在進入Spring的細節(jié)之前,我們將首先通過示例Java代碼說明這個概念:
public class FactoryTest {// Test method which is the client@Testpublic void test() {Kitchen factory = new KitchenFactory();KitchenMeal meal = factory.getMeal("P.1");KitchenMeal dessert = factory.getDessert("I.1");assertTrue("Meal's name should be 'protein meal' and was '" +meal.getName() + "'", meal.getName().equals("protein meal"));assertTrue("Dessert's name should be 'ice-cream' and was '" +dessert.getName() + "'", dessert.getName().equals("ice-cream"));} }// abstract factory abstract class Kitchen {public abstract KitchenMeal getMeal(String preferency);public abstract KitchenMeal getDessert(String preferency); }// concrete factory class KitchenFactory extends Kitchen {@Overridepublic KitchenMeal getMeal(String preferency) {if (preferency.equals("F.1")) {return new FastFoodMeal();} else if (preferency.equals("P.1")) {return new ProteinMeal();}return new VegetarianMeal();}@Overridepublic KitchenMeal getDessert(String preferency) {if (preferency.equals("I.1")) {return new IceCreamMeal();}return null;} }// abstract product abstract class KitchenMeal {public abstract String getName(); }// concrete products class ProteinMeal extends KitchenMeal {@Overridepublic String getName() {return "protein meal";} }class VegetarianMeal extends KitchenMeal {@Overridepublic String getName() {return "vegetarian meal";} }class FastFoodMeal extends KitchenMeal {@Overridepublic String getName() {return "fast-food meal";} }class IceCreamMeal extends KitchenMeal {@Overridepublic String getName() {return "ice-cream";} }我們可以在這個例子中看到,抽象工廠封裝了對象的創(chuàng)建。對象創(chuàng)建可以使用與經(jīng)典構(gòu)造函數(shù)一樣使用的工廠方法模式。在Spring中,工廠的例子是org.springframework.beans.factory.BeanFactory。通過它的實現(xiàn),我們可以從Spring的容器訪問bean。根據(jù)采用的策略,getBean方法可以返回已創(chuàng)建的對象(共享實例,單例作用域)或初始化新的對象(原型作用域)。在BeanFactory的實現(xiàn)中,我們可以區(qū)分:ClassPathXmlApplicationContext,XmlWebApplicationContext,StaticWebApplicationContext,StaticPortletApplicationContext,GenericApplicationContext,StaticApplicationContext。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"file:test-context.xml"} ) public class TestProduct {@Autowiredprivate BeanFactory factory;@Testpublic void test() {System.out.println("Concrete factory is: " + factory.getClass());assertTrue("Factory can't be null", factory != null);ShoppingCart cart = (ShoppingCart) factory.getBean("shoppingCart");assertTrue("Shopping cart object can't be null", cart != null);System.out.println("Found shopping cart bean:" + cart.getClass());} }在這種情況下,抽象工廠由BeanFactory接口表示。具體工廠是在第一個System.out中打印的,是org.springframework.beans.factory.support.DefaultListableBeanFactory的實例。它的抽象產(chǎn)物是一個對象。在我們的例子中,具體的產(chǎn)品就是被強轉(zhuǎn)為ShoppingCart實例的抽象產(chǎn)品(Object)。
第一篇文章介紹了通過設(shè)計模式來正確組織的我們實現(xiàn)良好的編程風(fēng)格。在這里,我們可以看到在Spring框架中使用解釋器,構(gòu)建器,工廠方法和工廠。第一個是幫助解釋以SpEL表達的文本。三個最后的模式屬于創(chuàng)建設(shè)計模式的三劍客,它們在Spring中的主要目的是簡化對象的創(chuàng)建。他們通過分解復(fù)雜對象(構(gòu)建器)的初始化或通過集中在公共點的初始化來做到對象的創(chuàng)建(要不然怎么叫工廠呢,必須有通用點的)。
總結(jié)
以上是生活随笔為你收集整理的Spring框架中的设计模式(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java互联网架构-京东国美高并发核心技
- 下一篇: Spring框架中的设计模式(三)