1、Manage and Configure POJOs with the Spring IoC Container
创建POPJ Class
package com.apress.springrecipes.sequence; public class SequenceGenerator { private String prefix; private String suffix; private int initial; private int counter; public SequenceGenerator() {} public SequenceGenerator(String prefix, String suffix, int initial) { this.prefix = prefix; this.suffix = suffix; this.initial = initial; } public void setPrefix(String prefix) { this.prefix = prefix; } public void setSuffix(String suffix) { this.suffix = suffix; } public void setInitial(int initial) { this.initial = initial; } public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefix); buffer.append(initial + counter++); buffer.append(suffix); return buffer.toString(); } }
XML配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="suffix" value="A" /> <property name="initial" value="100000" /> </bean </beans>
实例化Spring IoC Container,并通过IoC Container创建POPJ
package com.apress.springrecipes.sequence; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator"); System.out.println(generator.getSequence()); System.out.println(generator.getSequence()); } }
2、Create POJOs by Invoking a Constructor
Product.java
package com.apress.springrecipes.shop; public abstract class Product { private String name; private double price; public Product() {} public Product(String name, double price) { this.name = name; this.price = price; } // Getters and Setters public void setName(String name) { this.name = name; } public void setPrice(double price) { this.price = price; } public String getName() { return name; } public double getPrice() { return price; } public String toString() { return name + " " + price; } }
Dis.java
package com.apress.springrecipes.shop; public class Disc extends Product { private int capacity; public Disc() { super(); } public Disc(String name, double price) { super(name, price); } // Getters and Setters public void setCapacity(int capacity) { this.capacity = capacity; } public int getCapacity() { return capacity; } }
Battery.java
package com.apress.springrecipes.shop; public class Battery extends Product { private boolean rechargeable; public Battery() { super(); } public Battery(String name, double price) { super(name, price); } // Getters and Setters public void setRechargeable(boolean rechargeable) { this.rechargeable = rechargeable; } public boolean getRechargeable() { return rechargeable; } }
配置beans.xml
<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-3.2.xsd"> <!-- 当没有constructor-arg时,调用默认构造器创建bean,否则寻找最合适的构造器; 然后再property的属性通过setter方法初始化 --> <!-- Product aaa = new Battery(); aaa.setName("AAA"); aaa.setPrice(2.5); aaa.setRechargeable(true); Product cdrw = new Disc(); cdrw.setName("CD-RW"); cdrw.setPrice(1.5); cdrw.setCapacity(700); --> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <!-- Product aaa = new Battery("AAA", 2.5); aaa.setRechargeable(true); Product cdrw = new Disc("CD-RW", 1.5); cdrw.setCapacity(700); --> <!-- constructor-arg标签可以通过添加type属性(int、java.lang.String等)、 name属性(形参名词)、index属性(参数顺序)等来进一步限定,进而找到最合适的构造器。 --> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <constructor-arg value="AAA" /> <constructor-arg value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <constructor-arg value="CD-RW" /> <constructor-arg value="1.5" /> <property name="capacity" value="700" /> </bean> </beans>
实例化
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); System.out.println(aaa); System.out.println(cdrw); } }
3、Use POJO References, Auto-Wiring, and Imports to Interact with Other POJOs
SequenceGenerator引用了一个PrefixGenerator
package com.apress.springrecipes.sequence; public interface PrefixGenerator { public String getPrefix(); }
package com.apress.springrecipes.sequence; import java.text.SimpleDateFormat; import java.text.DateFormat; import java.util.Date; public class DatePrefixGenerator implements PrefixGenerator { private DateFormat formatter; public void setPattern(String pattern) { this.formatter = new SimpleDateFormat(pattern); } public String getPrefix() { return formatter.format(new Date()); } }
package com.apress.springrecipes.sequence; public class SequenceGenerator { private PrefixGenerator prefixGenerator; private String suffix; private int initial; private int counter; public SequenceGenerator() {} public SequenceGenerator(PrefixGenerator prefixGenerator, String suffix, int initial) { this.prefixGenerator = prefixGenerator; this.suffix = suffix; this.initial = initial; } public void setPrefixGenerator(PrefixGenerator prefixGenerator) { this.prefixGenerator = prefixGenerator; } public void setSuffix(String suffix) { this.suffix = suffix; } public void setInitial(int initial) { this.initial = initial; } public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefixGenerator.getPrefix()); buffer.append(initial + counter++); buffer.append(suffix); return buffer.toString(); } }
配置XML
<!-- 引用一个bean --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="datePrefixGenerator" class="com.apress.springrecipes.sequence.DatePrefixGenerator"> <property name="pattern" value="yyyyMMdd" /> </bean> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="initial" value="100000" /> <property name="suffix" value="A" /> <property name="prefixGenerator" ref="datePrefixGenerator" /> </bean> </beans> <!--constructor-arg 引用bean--> <!-- <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <constructor-arg ref="datePrefixGenerator" /> <property name="initial" value="100000" /> <property name="suffix" value="A" /> </bean> --> <!-- 用一个自定义的标签p声明 --> <!-- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="datePrefixGenerator" class="com.apress.springrecipes.sequence.DatePrefixGenerator"> <property name="pattern" value="yyyyMMdd" /> </bean> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator" p:suffix="A" p:initial="1000000" p:prefixGenerator-ref="datePrefixGenerator" /> </beans> --> <!-- 引用内部bean--> <!-- <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="initial" value="100000" /> <property name="suffix" value="A" /> <property name="prefixGenerator"> <bean class="com.apress.springrecipes.sequence.DatePrefixGenerator"> <property name="pattern" value="yyyyMMdd" /> </bean> </property> </bean> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <constructor-arg> <bean class="com.apress.springrecipes.sequence.DatePrefixGenerator"> <property name="pattern" value="yyyyMMdd" /> </bean> </constructor-arg> <property name="initial" value="100000" /> <property name="suffix" value="A" /> </bean> -->
使用bean
package com.apress.springrecipes.sequence; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator"); System.out.println(generator.getSequence()); System.out.println(generator.getSequence()); } }
4、Configure POJOs with Java Collection Attributes
有集合属性的POPJ Class
package com.apress.springrecipes.sequence; import java.util.List; public class SequenceGenerator { private String prefix; private List<Object> suffixes; private int initial; private int counter; public SequenceGenerator() {} public SequenceGenerator(String prefix, List<Object> suffixes, int initial) { this.prefix = prefix; this.suffixes = suffixes; this.initial = initial; } public void setPrefix(String prefix) { this.prefix = prefix; } public void setSuffixes(List<Object> suffixes) { this.suffixes = suffixes; } public void setInitial(int initial) { this.initial = initial; } public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefix); buffer.append(initial + counter++); for (Object suffix : suffixes) { buffer.append("-"); buffer.append(suffix); } return buffer.toString(); } }
XML配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <!-- List标签下的每一项可以是value(常量值)、ref(引用的bean)、 idref、<bean>(匿名内部bean)、<null/>(空值); Set与List的唯一区别是Set中的元素不会重复,也就意味着相同的值会被覆盖 --> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="initial" value="100000" /> <property name="suffixes"> <list> <value>A</value> <bean class="java.net.URL"> <constructor-arg value="http" /> <constructor-arg value="www.apress.com" /> <constructor-arg value="/" /> </bean> <null /> </list> </property> </bean> <!-- Map的定义方式为<key>和Value --> <!-- <bean id="sequenceGenerator"class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="initial" value="100000" /> <property name="suffixes"> <map> <entry> <key> <value>type</value> </key> <value>A</value> </entry> <entry key="type1" value="A" /> <entry> <key> <value>url</value> </key> <bean class="java.net.URL"> <constructor-arg value="http" /> <constructor-arg value="www.apress.com" /> <constructor-arg value="/" /> </bean> </entry> <entry key="url1"> <bean class="java.net.URL"> <constructor-arg value="http" /> <constructor-arg value="www.apress.com" /> <constructor-arg value="/" /> </bean> </entry> </map> </property> </bean> --> <!-- Properties与Map的区别为:Properties要求键值对均为String --> <!-- <bean id="sequenceGenerator"class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="initial" value="100000" /> <property name="suffixes"> <props> <prop key="type">A</prop> <prop key="url">http://www.apress.com/</prop> <prop key="null">null</prop> </props> </property> </bean> --> <!-- 指定集合元素类型type --> <!-- <property name="suffixes"> <list> <value type="int">5</value> <value type="int">10</value> <value type="int">20</value> </list> </property> --> <!-- 指定具体使用的集合对象,例如指定Set为TreeSet --> <!-- <property name="suffixes"> <bean class="org.springframework.beans.factory.config.SetFactoryBean"> <property name="targetSetClass"> <value>java.util.TreeSet</value> </property> <property name="sourceSet"> <set> <value>5</value> <value>10</value> <value>20</value> </set> </property> </bean> </property> --> <!-- 指定具体使用的集合对象,例如指定Set为TreeSet,使用util标签 --> <!-- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="initial" value="100000" /> <property name="suffixes"> <util:set set-class="java.util.TreeSet"> <value>5</value> <value>10</value> <value>20</value> </util:set> </property> </bean> </beans> --> </beans>
使用bean
package com.apress.springrecipes.sequence; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator"); System.out.println(generator.getSequence()); System.out.println(generator.getSequence()); } }
5、Set a POJOs Scope
Spring中Scope分以下几种:
singleton -- Creates a single bean instance per Spring IoC container
prototype -- Creates a new bean instance each time when requested
request -- Creates a single bean instance per HTTP request; only valid in the context of a web application
session -- Creates a single bean instance per HTTP session; only valid in the context of a web application
globalSession -- Creates a single bean instance per global HTTP session; only valid in the context of a portalapplication
package com.apress.springrecipes.shop; public abstract class Product { private String name; private double price; public Product() {} public Product(String name, double price) { this.name = name; this.price = price; } // Getters and Setters public void setName(String name) { this.name = name; } public void setPrice(double price) { this.price = price; } public String getName() { return name; } public double getPrice() { return price; } public String toString() { return name + " " + price; } }
package com.apress.springrecipes.shop; public class Disc extends Product { private int capacity; public Disc() { super(); } public Disc(String name, double price) { super(name, price); } // Getters and Setters public void setCapacity(int capacity) { this.capacity = capacity; } public int getCapacity() { return capacity; } }
package com.apress.springrecipes.shop; public class Battery extends Product { private boolean rechargeable; public Battery() { super(); } public Battery(String name, double price) { super(name, price); } // Getters and Setters public void setRechargeable(boolean rechargeable) { this.rechargeable = rechargeable; } public boolean getRechargeable() { return rechargeable; } }
package com.apress.springrecipes.shop; import java.util.List; import java.util.ArrayList; public class ShoppingCart { private List<Product> items = new ArrayList<Product>(); public void addItem(Product item) { items.add(item); } public List<Product> getItems() { return items; } }
配置XML,默认scope为singleton
<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-3.2.xsd"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" /> </beans>
main方法,执行结果为:
Shopping cart 1 contains [AAA 2.5, CD-RW 1.5]
Shopping cart 2 contains [AAA 2.5, CD-RW 1.5, DVD-RW 3.0]
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); Product dvdrw = (Product) context.getBean("dvdrw"); ShoppingCart cart1 = (ShoppingCart) context.getBean("shoppingCart"); cart1.addItem(aaa); cart1.addItem(cdrw); System.out.println("Shopping cart 1 contains " + cart1.getItems()); ShoppingCart cart2 = (ShoppingCart) context.getBean("shoppingCart"); cart2.addItem(dvdrw); System.out.println("Shopping cart 2 contains " + cart2.getItems()); } }
以上shoppingCart的配置相当于:
<bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="singleton" />
如果shoppingCart配置为:
<bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" />
main方法执行结果为:
Shopping cart 1 contains [AAA 2.5, CD-RW 1.5]
Shopping cart 2 contains [DVD-RW 3.0]
2.6 Use Data from External Resources
读取java classpath路径下的properties文件,文件内容:
specialcustomer.discount=0.1 summer.discount=0.15 endofyear.discount=0.2
在配置文件中读取properties的内容:
<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-3.2.xsd"> <bean id="discountPropertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="location"> <value>classpath:discounts.properties</value> </property> <property name="ignoreResourceNotFound" value="true"/> <property name="ignoreUnresolvablePlaceholders" value="true"/> </bean> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="discount" value="${specialcustomer.discount:0}" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="discount" value="${summer.discount:0}" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="discount" value="${endofyear.discount:0}" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> </beans>
也可以在java代码中读取:
public class Main { public static void main(String[] args) throws Exception { Resource resource = new ClassPathResource("discounts.properties"); Properties props = PropertiesLoaderUtils.loadProperties(resource); System.out.println("And don't forget our discounts!"); System.out.println(props); } }
也可以直接加载某路径下的文件 或者 URL获取的文件:
Resource resource = new FileSystemResource("c:/shop/banner.txt") Resource resource = new UrlResource("http://www.apress.com/context/banner.txt")
读取classpath下的文件:
************************* * Welcome to My Shop! * *************************
设置一个POPJ类:
package com.apress.springrecipes.shop; import org.springframework.core.io.Resource; import java.io.IOException; import java.io.InputStream; import java.io.BufferedReader; import java.io.InputStreamReader; public class BannerLoader { private Resource banner; public void setBanner(Resource banner) { this.banner = banner; } public void showBanner() throws IOException { InputStream in = banner.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); while (true) { String line = reader.readLine(); if (line == null) break; System.out.println(line); } reader.close(); } }
初始化bean时,读取数据:
<bean id="bannerLoader" class="com.apress.springrecipes.shop.BannerLoader" init-method="showBanner"> <property name="banner"> <value>classpath:banner.txt</value> </property> </bean>
设置路径时候,还可以是:
file:c:/shop/banner.txt
classpath:banner.txt
http://springrecipes.apress.com/shop/banner.txt
2.7 Resolve I18N Text Messages for Different Locales in Properties Files
国际化文件:
配置国际化文件对应的bean
<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-3.2.xsd"> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>classpath:messages</value> </list> </property> <property name="cacheSeconds" value="1"/> </bean> </beans>
使用国际化:
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import java.util.Locale; import java.util.Date; public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); String alert = context.getMessage("alert.checkout", null, Locale.US); String alert_inventory = context.getMessage("alert.inventory.checkout", new Object[] { "[DVD-RW 3.0]", new Date() }, Locale.US); System.out.println("The I18N message for alert.checkout is: " + alert); System.out.println("The I18N message for alert.inventory.checkout is: " + alert_inventory); } }
2-8. Customize POJO Initialization and Destruction
初始化bean时的方法init-method和销毁bean时的方法destroy-method
Product.java、Battery.java、Disc.java同上
package com.apress.springrecipes.shop; import java.util.List; import java.util.ArrayList; public class ShoppingCart { private List<Product> items = new ArrayList<Product>(); public void addItem(Product item) { items.add(item); } public List<Product> getItems() { return items; } }
package com.apress.springrecipes.shop; import java.io.IOException; import java.io.File; import java.io.BufferedWriter; import java.io.OutputStreamWriter; import java.io.FileOutputStream; import java.util.Date; public class Cashier { private String fileName; private String path; private BufferedWriter writer; public void setFileName(String fileName) { this.fileName = fileName; } public void setPath(String path) { this.path = path; } public void openFile() throws IOException { File targetDir = new File(path); if (!targetDir.exists()) { targetDir.mkdir(); } File checkoutFile = new File(path, fileName + ".txt"); if(!checkoutFile.exists()) { checkoutFile.createNewFile(); } writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(checkoutFile, true))); } public void checkout(ShoppingCart cart) throws IOException { writer.write(new Date() + "\t" +cart.getItems() + "\r\n"); writer.flush(); } public void closeFile() throws IOException { writer.close(); } }配置XML
<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-3.2.xsd"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" lazy-init="true" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> <bean id="cashier" class="com.apress.springrecipes.shop.Cashier" init-method="openFile" destroy-method="closeFile"> <property name="fileName" value="checkout" /> <property name="path" value="c:/Windows/Temp/cashier" /> </bean> </beans>lazy-init默认为false,如果为true,意味着,如果bean一直没有被应用初始化,那么,bean不会被初始化。
初始化bean时候会执行init-method,在bean最后销毁时会执行destroy-method。
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); Product dvdrw = (Product) context.getBean("dvdrw"); ShoppingCart cart1 = (ShoppingCart) context.getBean("shoppingCart"); cart1.addItem(aaa); cart1.addItem(cdrw); System.out.println("Shopping cart 1 contains " + cart1.getItems()); ShoppingCart cart2 = (ShoppingCart) context.getBean("shoppingCart"); cart2.addItem(dvdrw); System.out.println("Shopping cart 2 contains " + cart2.getItems()); Cashier cashier = (Cashier) context.getBean("cashier"); cashier.checkout(cart1); cashier.checkout(cart2); } }
如果希望一个bean初始化时比另外一个先,可以:
<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-3.2.xsd"> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator" autowire="byType" depends-on="datePrefixGenerator"> <property name="initial" value="100000" /> <property name="suffix" value="A" /> </bean> <bean id="datePrefixGenerator" primary="true" class="com.apress.springrecipes.sequence.DatePrefixGenerator"> </bean> <bean id="numberprefixGenerator" class="com.apress.springrecipes.sequence.NumberPrefixGenerator"> </bean> </beans>示例中datePrefixGenerator会比sequenceGenerator先创建。
2.9 Create Post Processors to Validate and Modify POJOs
处理每个bean,可以继承BeanPostProcessor
Product.java、Battery.java、Disc.java、Cashier.java 、ShoppingCart.java 、Main.java均同2.8例,添加如下两个类,这两个类均继承BeanPostProcessor,这意味着每个bean都会被这两个类处理,该类有两方法,一个是初始化之前处理,一个是之后。
package com.apress.springrecipes.shop; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class AuditCheckBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("In AuditCheckBeanPostProcessor.postProcessBeforeInitialization, processing bean type: " + bean.getClass()); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
package com.apress.springrecipes.shop; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class ProductCheckBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Product) { String productName = ((Product) bean).getName(); System.out.println("In ProductCheckBeanPostProcessor.postProcessBeforeInitialization, processing Product: " + productName); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Product) { String productName = ((Product) bean).getName(); System.out.println("In ProductCheckBeanPostProcessor.postProcessAfterInitialization, processing Product: " + productName); } return bean; } }在配置文件中需要配置相应的bean
<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-3.2.xsd"> <bean class="com.apress.springrecipes.shop.ProductCheckBeanPostProcessor"/> <bean class="com.apress.springrecipes.shop.AuditCheckBeanPostProcessor"/> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> <bean id="cashier" class="com.apress.springrecipes.shop.Cashier" init-method="openFile" destroy-method="closeFile"> <property name="fileName" value="checkout" /> <property name="path" value="c:/Windows/Temp/cashier" /> </bean> </beans>
2-9. Create Post Processors to Validate and Modify POJOs
使用静态工厂方法初始化bean
package com.apress.springrecipes.shop; public class ProductCreator { public static Product createProduct(String productId) { if ("aaa".equals(productId)) { return new Battery("AAA", 2.5); } else if ("cdrw".equals(productId)) { return new Disc("CD-RW", 1.5); } else if ("dvdrw".equals(productId)) { return new Disc("DVD-RW", 3.0); } throw new IllegalArgumentException("Unknown product"); } }
<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-3.2.xsd"> <bean id="aaa" class="com.apress.springrecipes.shop.ProductCreator" factory-method="createProduct"> <constructor-arg value="aaa" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.ProductCreator" factory-method="createProduct"> <constructor-arg value="cdrw" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.ProductCreator" factory-method="createProduct"> <constructor-arg value="dvdrw" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> </beans>通过某个bean的方法创建新的bean,例如
package com.apress.springrecipes.shop; import java.util.Map; public class ProductCreator { private Map<String, Product> products; public void setProducts(Map<String, Product> products) { this.products = products; } public Product createProduct(String productId) { Product product = products.get(productId); if (product != null) { return product; } throw new IllegalArgumentException("Unknown product"); } }
<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-3.2.xsd"> <bean id="productCreator" class="com.apress.springrecipes.shop.ProductCreator"> <property name="products"> <map> <entry key="aaa"> <bean class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> </bean> </entry> <entry key="cdrw"> <bean class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> </bean> </entry> <entry key="dvdrw"> <bean class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> </bean> </entry> </map> </property> </bean> <bean id="aaa" factory-bean="productCreator" factory-method="createProduct"> <constructor-arg value="aaa" /> </bean> <bean id="cdrw" factory-bean="productCreator" factory-method="createProduct"> <constructor-arg value="cdrw" /> </bean> <bean id="dvdrw" factory-bean="productCreator" factory-method="createProduct"> <constructor-arg value="dvdrw" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> </beans>配置中,'aaa','cdrw','dvdrw'是通过id为productCreator的bean 的createProduct方法创建的。
2-11. Use Spring Environments and Profiles to Load Different Sets of POJOs
可以通过profile的给不同的配置加标签,然后根据实际情况激活。示例中profile分为五中:spring、summer、autumn、winter以及global,
<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-3.2.xsd"> <beans profile="spring"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> </beans> <beans profile="summer,winter"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.0" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.0" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="2.5" /> <property name="capacity" value="700" /> </bean> </beans> <beans profile="autumn"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> </beans> <beans profile="global"> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> <bean id="cashier" class="com.apress.springrecipes.shop.Cashier" init-method="openFile" destroy-method="closeFile"> <property name="fileName" value="checkout" /> <property name="path" value="c:/Windows/Temp/cashier" /> </bean> </beans> </beans>激活global和winter
package com.apress.springrecipes.shop; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) throws Exception { GenericXmlApplicationContext context = new GenericXmlApplicationContext(); context.getEnvironment().setActiveProfiles("global","winter"); context.load("beans.xml"); context.refresh(); Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); Product dvdrw = (Product) context.getBean("dvdrw"); ShoppingCart cart1 = (ShoppingCart) context.getBean("shoppingCart"); cart1.addItem(aaa); cart1.addItem(cdrw); System.out.println("Shopping cart 1 contains " + cart1.getItems()); ShoppingCart cart2 = (ShoppingCart) context.getBean("shoppingCart"); cart2.addItem(dvdrw); System.out.println("Shopping cart 2 contains " + cart2.getItems()); Cashier cashier = (Cashier) context.getBean("cashier"); cashier.checkout(cart1); cashier.checkout(cart2); } }也可以用java命令的参数:-Dspring.profiles.active="global,winter"来执行。也可以在Spring的Servlet的 web.xml的配置来激活:
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name> spring.profiles.active</param-name> <param-value>winter</param-value> </init-param> </servlet>2-12. Aspect Orientated Programming 面向切面编程的目的:关注自身业务,分理出非自身应该做的事物,例如,安全、事物、日志等。面向切面编程中的主要概念有: Advice(通知):执行的操作,例如记录日志。 JoinPoint(连接点):可以使用Advice的地方,例如每个方法前后、抛出异常时。 PointCut(切入点):连接点可能很多,但是真正希望使用Advice的地方可能只有几个,可以用切入点来定义帅选连接点。 Aspect(切面):切面是Advice和PointCut的结合,它是AOP的基本单元,定义了做什么(Advice)、什么时候做(方法前后或抛出异常时)、那里做(PointCut)等。 Introduction(引入):允许向类中加入新方法、属性,即把切面引入到目标类中。 Proxy(代理):实现AOP的机制。 Waeving(织入):实现AOP机制的过程,Spring AOP是动态的。 先定义一组POJO
package com.apress.springrecipes.calculator; public interface ArithmeticCalculator { public double add(double a, double b); public double sub(double a, double b); public double mul(double a, double b); public double div(double a, double b); }
package com.apress.springrecipes.calculator; public interface UnitCalculator { public double kilogramToPound(double kilogram); public double kilometerToMile(double kilometer); }
package com.apress.springrecipes.calculator; public class ArithmeticCalculatorImpl implements ArithmeticCalculator { public double add(double a, double b) { double result = a + b; System.out.println(a + " + " + b + " = " + result); return result; } public double sub(double a, double b) { double result = a - b; System.out.println(a + " - " + b + " = " + result); return result; } public double mul(double a, double b) { double result = a * b; System.out.println(a + " * " + b + " = " + result); return result; } public double div(double a, double b) { if (b == 0) { throw new IllegalArgumentException("Division by zero"); } double result = a / b; System.out.println(a + " / " + b + " = " + result); return result; } }
package com.apress.springrecipes.calculator; public class UnitCalculatorImpl implements UnitCalculator { public double kilogramToPound(double kilogram) { double pound = kilogram * 2.2; System.out.println(kilogram + " kilogram = " + pound + " pound"); return pound; } public double kilometerToMile(double kilometer) { double mile = kilometer * 0.62; System.out.println(kilometer + " kilometer = " + mile + " mile"); return mile; } }定义切面,实现的方法中分别对应了什么时候记录日志
package com.apress.springrecipes.calculator; import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class CalculatorLoggingAspect { private Log log = LogFactory.getLog(this.getClass()); public void logBefore(JoinPoint joinPoint) { log.info("The method " + joinPoint.getSignature().getName() + "() begins with " + Arrays.toString(joinPoint.getArgs())); } public void logAfter(JoinPoint joinPoint) { log.info("The method " + joinPoint.getSignature().getName() + "() ends"); } public void logAfterReturning(JoinPoint joinPoint, Object result) { log.info("The method " + joinPoint.getSignature().getName() + "() ends with " + result); } public void logAfterThrowing(JoinPoint joinPoint, IllegalArgumentException e) { log.error("Illegal argument " + Arrays.toString(joinPoint.getArgs()) + " in " + joinPoint.getSignature().getName() + "()"); } public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { log.info("The method " + joinPoint.getSignature().getName() + "() begins with " + Arrays.toString(joinPoint.getArgs())); try { Object result = joinPoint.proceed(); log.info("The method " + joinPoint.getSignature().getName() + "() ends with " + result); return result; } catch (IllegalArgumentException e) { log.error("Illegal argument " + Arrays.toString(joinPoint.getArgs()) + " in " + joinPoint.getSignature().getName() + "()"); throw e; } } }日志的配置文件:
# Set root logger level to INFO and its only appender to A1. log4j.rootLogger=INFO, A1 # A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n配置切面、切点、通知:(定义的切点为com.apress.springrecipes.calculator.ArithmeticCalculator以及com.apress.springrecipes.calculator.UnitCalculator执行的全部方法,关于切入点如何定义,可以查找更多资料)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <aop:config> <aop:aspect id="loggingAspect" ref="calculatorLoggingAspect"> <aop:pointcut id="loggingOperation" expression="within(com.apress.springrecipes.calculator.ArithmeticCalculator+) || within(com.apress.springrecipes.calculator.UnitCalculator+)" /> <aop:before pointcut-ref="loggingOperation" method="logBefore" /> <aop:after pointcut-ref="loggingOperation" method="logAfter" /> <aop:after-returning pointcut-ref="loggingOperation" returning="result" method="logAfterReturning" /> <aop:after-throwing pointcut-ref="loggingOperation" throwing="e" method="logAfterThrowing" /> <aop:around pointcut-ref="loggingOperation" method="logAround" /> </aop:aspect> </aop:config> <bean id="calculatorLoggingAspect" class="com.apress.springrecipes.calculator.CalculatorLoggingAspect" /> <bean id="arithmeticCalculator" class="com.apress.springrecipes.calculator.ArithmeticCalculatorImpl" /> <bean id="unitCalculator" class="com.apress.springrecipes.calculator.UnitCalculatorImpl" /> </beans>main方法中没有记录日志,但是事实上记录记录了日志:
package com.apress.springrecipes.calculator; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) context.getBean("arithmeticCalculator"); arithmeticCalculator.add(1, 2); arithmeticCalculator.sub(4, 3); arithmeticCalculator.mul(2, 3); arithmeticCalculator.div(4, 2); UnitCalculator unitCalculator = (UnitCalculator) context.getBean("unitCalculator"); unitCalculator.kilogramToPound(10); unitCalculator.kilometerToMile(5); } }2-13. Declare POJOs from Static Fields or Object Properties 静态属性构造POJO
package com.apress.springrecipes.shop; public abstract class Product { public static final Product AAA = new Battery("AAA", 2.5); public static final Product CDRW = new Disc("CD-RW", 1.5); ... }
<beans ...> <bean id="aaa" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> <property name="staticField"> <value>com.apress.springrecipes.shop.Product.AAA</value> </property> </bean> <bean id="cdrw" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> <property name="staticField"> <value>com.apress.springrecipes.shop.Product.CDRW</value> </property> </bean> </beans>该配置相当于:
还可以用如下配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <util:constant id="aaa" static-field="com.apress.springrecipes.shop.Product.AAA" /> <util:constant id="cdrw" static-field="com.apress.springrecipes.shop.Product.CDRW" /> </beans>
通过属性构造bean
package com.apress.springrecipes.shop; public class ProductRanking { private Product bestSeller; public Product getBestSeller() { return bestSeller; } public void setBestSeller(Product bestSeller) { this.bestSeller = bestSeller; } }
获取bean的属性bestSeller,通过它构造一个bean:
<beans ...> <bean id="productRanking" class="com.apress.springrecipes.shop.ProductRanking"> <property name="bestSeller"> <bean class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> </bean> </property> </bean> <bean id="bestSeller" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"> <property name="targetObject" ref="productRanking" /> <property name="propertyPath" value="bestSeller" /> </bean> </beans>
相当于Product bestSeller = productRanking.getBestSeller();
还可以如下定义:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> ... <util:property-path id="bestSeller" path="productRanking.bestSeller" /> </beans>
2-14. Making POJOs Aware of Spring’s IoC Container Resources Cashier将文件的路径通过path属性注入,进而访问到外部文件资源
<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-3.2.xsd"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> <bean id="cashier" class="com.apress.springrecipes.shop.Cashier" init-method="openFile" destroy-method="closeFile"> <property name="path" value="c:/Windows/Temp/cashier" /> </bean> </beans>
2-15. Communicate Application Events Between POJOs
构造beans,Product.java、Battery.java、Disc.java、SoppingCart.java同上
package com.apress.springrecipes.shop; import java.util.List; import java.util.ArrayList; public class ShoppingCart { private List<Product> items = new ArrayList<Product>(); public void addItem(Product item) { items.add(item); } public List<Product> getItems() { return items; } }定义Event
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationEvent; import java.util.Date; public class CheckoutEvent extends ApplicationEvent { private Date time; public CheckoutEvent(Object source, Date time) { super(source); this.time = time; } public Date getTime() { return time; } }
发布Event
package com.apress.springrecipes.shop; import java.io.IOException; import java.io.File; import java.io.BufferedWriter; import java.io.OutputStreamWriter; import java.io.FileOutputStream; import java.util.Date; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; public class Cashier implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; public void setApplicationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void checkout(ShoppingCart cart) throws IOException { CheckoutEvent event = new CheckoutEvent(this, new Date()); applicationEventPublisher.publishEvent(event); } }监听Event.java
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationListener; import java.util.Date; public class CheckoutListener implements ApplicationListener<CheckoutEvent> { public void onApplicationEvent(CheckoutEvent event) { Date time = event.getTime(); // Do anything you like with the checkout amount and time System.out.println("Checkout event [" + time + "]"); } }
测试Main
package com.apress.springrecipes.shop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new GenericXmlApplicationContext("beans.xml"); Product aaa = (Product) context.getBean("aaa"); Product cdrw = (Product) context.getBean("cdrw"); Product dvdrw = (Product) context.getBean("dvdrw"); ShoppingCart cart1 = (ShoppingCart) context.getBean("shoppingCart"); cart1.addItem(aaa); cart1.addItem(cdrw); System.out.println("Shopping cart 1 contains " + cart1.getItems()); ShoppingCart cart2 = (ShoppingCart) context.getBean("shoppingCart"); cart2.addItem(dvdrw); System.out.println("Shopping cart 2 contains " + cart2.getItems()); Cashier cashier = (Cashier) context.getBean("cashier"); cashier.checkout(cart1); cashier.checkout(cart2); } }
每次调用checkout的时候都会发布一个事件,同时被CheckoutListener监听。
配置文件如下:(需要配置监听器的bean)
<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-3.2.xsd"> <bean id="aaa" class="com.apress.springrecipes.shop.Battery"> <property name="name" value="AAA" /> <property name="price" value="2.5" /> <property name="rechargeable" value="true" /> </bean> <bean id="cdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> <property name="capacity" value="700" /> </bean> <bean id="dvdrw" class="com.apress.springrecipes.shop.Disc"> <property name="name" value="DVD-RW" /> <property name="price" value="3.0" /> <property name="capacity" value="700" /> </bean> <bean id="shoppingCart" class="com.apress.springrecipes.shop.ShoppingCart" scope="prototype" /> <bean id="cashier" class="com.apress.springrecipes.shop.Cashier"/> <bean class="com.apress.springrecipes.shop.CheckoutListener"/> </beans>
2-16. Use Property Editors in Spring
属性值与文本值之间的转换
package com.apress.springrecipes.shop; import java.util.Date; public class ProductRanking { private Product bestSeller; private Date fromDate; private Date toDate; ... ... }
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <bean id="dateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd" /> </bean> <bean id="productRanking" class="com.apress.springrecipes.shop.ProductRanking"> <property name="bestSeller"> <bean class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> </bean> </property> <property name="fromDate"> <bean factory-bean="dateFormat" factory-method="parse"> <constructor-arg value="2013-09-01" /> </bean> </property> <property name="toDate"> <bean factory-bean="dateFormat" factory-method="parse"> <constructor-arg value="2013-09-30" /> </bean> </property> </bean> <util:property-path id="bestSeller" path="productRanking.bestSeller" /> </beans>
示例中将2013-09-30、2013-09-1转换成了Data类型。
采用Spring 的Property Editor
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <bean id="dateEditor" class="org.springframework.beans.propertyeditors.CustomDateEditor"> <constructor-arg> <bean class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-MM-dd" /> </bean> </constructor-arg> <constructor-arg value="true" /> </bean> <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.Date"> <ref local="dateEditor" /> </entry> </map> </property> </bean> <bean id="productRanking" class="com.apress.springrecipes.shop.ProductRanking"> <property name="bestSeller"> <bean class="com.apress.springrecipes.shop.Disc"> <property name="name" value="CD-RW" /> <property name="price" value="1.5" /> </bean> </property> <property name="fromDate" value="2013-10-01" /> <property name="toDate" value="2013-10-31" /> </bean> <util:property-path id="bestSeller" path="productRanking.bestSeller" /> </beans>
配置中,CustomEditorConfigure中注册了键值对,key是要转换成的对象,value是PropertyEditor(转换规则),然后再property中就会自动识别。
也可以自定义转换规则:
package com.apress.springrecipes.shop; import java.beans.PropertyEditorSupport; public class ProductEditor extends PropertyEditorSupport { public String getAsText() { Product product = (Product) getValue(); return product.getClass().getName() + "," + product.getName() + "," + product.getPrice(); } public void setAsText(String text) throws IllegalArgumentException { String[] parts = text.split(","); try { Product product = (Product) Class.forName(parts[0]).newInstance(); product.setName(parts[1]); product.setPrice(Double.parseDouble(parts[2])); setValue(product); } catch (Exception e) { throw new IllegalArgumentException(e); } } }
定义的key为Product,value为ProductEditor,
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.Date"> <ref local="dateEditor" /> </entry> <entry key="com.apress.springrecipes.shop.Product"> <bean class="com.apress.springrecipes.shop.ProductEditor" /> </entry> </map> </property> </bean> <bean id="productRanking" class="com.apress.springrecipes.shop.ProductRanking"> <property name="bestSeller"> <value>com.apress.springrecipes.shop.Disc,CD-RW,1.5</value> </property> <property name="fromDate" value="2013-12-01" /> <property name="toDate" value="2013-12-31" /> </bean>
2-22. Use the Spring Expression Language to Configure POJOs
package com.apress.springrecipes.spel; import java.util.Properties; public class CommonData { private Properties commonProperties; private String userOS; private String userRegion; private double randomNumber; private String email; public Properties getCommonProperties() { return commonProperties; } public String getUserOS() { return userOS; } public String getUserRegion() { return userRegion; } public double getRandomNumber() { return randomNumber; } public String getEmail() { return email; } public void setCommonProperties(Properties commonProperties) { this.commonProperties = commonProperties; } public void setUserOS(String userOS) { this.userOS = userOS; } public void setUserRegion(String userRegion) { this.userRegion = userRegion; } public void setRandomNumber(double randomNumber) { this.randomNumber = randomNumber; } public void setEmail(String email) { this.email = email; } }
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <bean id="commonData" class="com.apress.springrecipes.spel.CommonData" p:commonProperties="#{@systemProperties}" p:userOS="#{@systemProperties['os.name']?:'unknown OS'}" p:userRegion="#{@systemProperties['user.region']?:'unknown region'}" p:randomNumber="#{ T(java.lang.Math).random() * 100.0 }" p:email="#{emailUtilities.email}"/> </beans>
示例中,XML使用SpeL
<bean id="emailUtilities" class="com.apress.springrecipes.spel.EmailUtilities" p:email="[email protected]" p:password="springrecipes" p:host="localhost:25"/> <bean id="commonData" ... p:email="#{emailUtilities.email}"/>
示例中,commonData使用了emailUtilities的属性的值。
也可以在代码中使用SpeL
package com.apress.springrecipes.spel; ... @Component public class CommonData { @Value("#{systemProperties}") private Properties commonProperties; @Value("#{systemProperties['os.name']?:'unknown OS'}") private String userOS; @Value("#{systemProperties['user.region']?:'unknown region'}") private String userRegion; //Setter & getter methods omitted }