当先锋百科网

首页 1 2 3 4 5 6 7
1Spring对应用程序可移植性的影响

不要凭空杜撰对可移植性的需求。在许多情况下,终端用户并不关心你的应用程序是否能在3个不同的控制反转容器上运行,而只要求他能稳定运行。


2管理bean的生命周期

两个生命周期时间与bean关系尤为重要:
postinitialization(初始化后)和predestruction(销毁前)。


(Spring并不管理那些被配置成非单例bean的生命周期)



指定初始化方法:
  1. packagecn.partner4java.init;
  2. publicclassSimpleBean {
  3. privateString name;
  4. privateintage =0;
  5. publicvoidinit(){
  6. if(name ==null){
  7. System.out.println("name为空");
  8. }
  9. if(age ==0){
  10. System.out.println("age为0");
  11. }
  12. }
  13. publicString getName() {
  14. returnname;
  15. }
  16. publicvoidsetName(String name) {
  17. this.name = name;
  18. }
  19. publicintgetAge() {
  20. returnage;
  21. }
  22. publicvoidsetAge(intage) {
  23. this.age = age;
  24. }
  25. }
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="simple1"class="cn.partner4java.init.SimpleBean"
  8. init-method="init">
  9. <property name="age"value="29"/>
  10. <property name="name"value="Dr. Jekyll"/>
  11. </bean>
  12. <bean id="simple2"class="cn.partner4java.init.SimpleBean"
  13. init-method="init">
  14. <property name="age"value="29"/>
  15. </bean>
  16. <bean id="simple3"class="cn.partner4java.init.SimpleBean"
  17. init-method="init">
  18. <property name="name"value="Mr. Hyde"/>
  19. </bean>
  20. </beans>

调用:
  1. //在调用获取的时候就会调用init,在调用init之前,已经使用控制反转设置方法依赖注入设置进数据
  2. BeanFactory beanFactory = getBeanFactory();
  3. System.out.println("simple1");
  4. beanFactory.getBean("simple1");
  5. System.out.println("simple2");
  6. beanFactory.getBean("simple2");
  7. System.out.println("simple3");
  8. beanFactory.getBean("simple3");
  9. //后台打印:
  10. //simple1
  11. //simple2
  12. //name为空
  13. //simple3
  14. //age为0




实现InitializingBean接口:
Spring中InitializingBean接口允许你在bean的代码中这样定义:你希望bean能接收到Spring已经完成对其配置的通知。就像使用初始化方法时一样,你可以借机检查bean的配置以确保其有效,若要必要,还可以给出默认值。
  1. publicclassSimpleBeanIBimplementsInitializingBean {
  2. privatestaticfinalString DEFAULT_NAME ="Jan Machacek";
  3. privateString name;
  4. privateintage =0;
  5. publicString getName() {
  6. returnname;
  7. }
  8. publicvoidsetName(String name) {
  9. this.name = name;
  10. }
  11. publicintgetAge() {
  12. returnage;
  13. }
  14. publicvoidsetAge(intage) {
  15. this.age = age;
  16. }
  17. @Override
  18. publicString toString() {
  19. finalStringBuilder sb =newStringBuilder();
  20. sb.append("SimpleBean");
  21. sb.append("{name='").append(name).append('\'');
  22. sb.append(", age=").append(age);
  23. sb.append('}');
  24. returnsb.toString();
  25. }
  26. publicvoidafterPropertiesSet()throwsException {
  27. System.out.println("initializing bean");
  28. if(this.name ==null) {
  29. System.out.println("No name specified, using default.");
  30. this.name = DEFAULT_NAME;
  31. }
  32. if(this.age ==0) {
  33. thrownewIllegalArgumentException("You must set the [age] property bean of type ["+ getClass().getName() +"]");
  34. }
  35. }
  36. }


决议顺序:
Spring首先调用InitializingBean.afterPropertiesSet()方法,然后再调用初始化方法。(建议如果存在顺序,都写入到afterPropertiesSet中调用。)


afterPropertiesSet方法和类的继承:
例子抽象父类实现了接口,但是把afterPropertiesSet定义成了final,不可被子类覆盖,也就是去实现一些通用的初始化,然后再调用了自己定义的initSimple()初始化方法。
父类:
  1. publicabstractclassSimpleBeanSupportimplementsInitializingBean {
  2. privateString value;
  3. protectedvoidinitSimple()throwsException {
  4. // do nothing
  5. }
  6. publicfinalvoidafterPropertiesSet()throwsException {
  7. Assert.notNull(this.value,"The [value] property of ["+ getClass().getName() +"] must be set.");
  8. initSimple();
  9. }
  10. publicfinalvoidsetValue(String value) {
  11. this.value = value;
  12. }
  13. protectedfinalString getValue() {
  14. returnthis.value;
  15. }
  16. }

继承:
  1. publicclassSoutSimpleBeanextendsSimpleBeanSupport {
  2. privateString person;
  3. protectedvoidinitSimple()throwsException {
  4. Assert.notNull(this.person,"The [person] property of ["+ getClass().getName() +"] must be set.");
  5. }
  6. publicvoidsetPerson(String person) {
  7. this.person = person;
  8. }
  9. @Override
  10. publicString toString() {
  11. returnString.format("%s says \"%s\"",this.person, getValue());
  12. }
  13. }

配置文件:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="simple1"class="com.apress.prospring2.ch04.lifecycle.SoutSimpleBean">
  8. <property name="person"value="Winston Churchill"/>
  9. <property name="value"value="This report, by its very length, defends itself against the risk of being read."/>
  10. </bean>
  11. </beans>



嵌入bean的销毁:
1、bean销毁时执行某一方法
  1. publicclassDestructiveBeanimplementsInitializingBean {
  2. privateInputStream is =null;
  3. privateString filePath =null;
  4. publicvoidafterPropertiesSet()throwsException {
  5. System.out.println("Initializing Bean");
  6. Assert.notNull(this.filePath,"The [filePath] property of ["+ getClass().getName() +"] must be set.");
  7. newFile(this.filePath).createNewFile();
  8. this.is =newFileInputStream(this.filePath);
  9. }
  10. publicvoiddestroy() {
  11. System.out.println("Destroying Bean");
  12. if(this.is !=null) {
  13. try{
  14. this.is.close();
  15. this.is =null;
  16. newFile(this.filePath).delete();
  17. }catch(IOException ex) {
  18. System.err.println("WARN: An IOException occured"
  19. +" while trying to close the InputStream");
  20. }
  21. }
  22. }
  23. publicvoidsetFilePath(String filePath) {
  24. this.filePath = filePath;
  25. }
  26. @Override
  27. publicString toString() {
  28. finalStringBuilder sb =newStringBuilder();
  29. sb.append("DestructiveBean");
  30. sb.append("{is=").append(is);
  31. sb.append(", filePath='").append(filePath).append('\'');
  32. sb.append('}');
  33. returnsb.toString();
  34. }
  35. }

配置文件:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="destructive"class="com.apress.prospring2.ch04.lifecycle.DestructiveBean"
  8. destroy-method="destroy">
  9. <property name="filePath"value="/tmp/prospring25"/>
  10. </bean>
  11. </beans>

2、实现DisposableBean接口
DisposableBean接口提供了destory方法。
  1. publicclassDestructiveBeanIimplementsInitializingBean, DisposableBean {
  2. privateInputStream is =null;
  3. privateString filePath =null;
  4. publicvoidafterPropertiesSet()throwsException {
  5. System.out.println("Initializing Bean");
  6. Assert.notNull(this.filePath,"The [filePath] property of ["+ getClass().getName() +"] must be set.");
  7. newFile(this.filePath).createNewFile();
  8. this.is =newFileInputStream(this.filePath);
  9. }
  10. publicvoiddestroy() {
  11. System.out.println("Destroying Bean");
  12. if(this.is !=null) {
  13. try{
  14. this.is.close();
  15. this.is =null;
  16. newFile(this.filePath).delete();
  17. }catch(IOException ex) {
  18. System.err.println("WARN: An IOException occured"
  19. +" while trying to close the InputStream");
  20. }
  21. }
  22. }
  23. publicvoidsetFilePath(String filePath) {
  24. this.filePath = filePath;
  25. }
  26. @Override
  27. publicString toString() {
  28. finalStringBuilder sb =newStringBuilder();
  29. sb.append("DestructiveBean");
  30. sb.append("{is=").append(is);
  31. sb.append(", filePath='").append(filePath).append('\'');
  32. sb.append('}');
  33. returnsb.toString();
  34. }
  35. }

配置文件:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="destructive"class="com.apress.prospring2.ch04.lifecycle.DestructiveBeanI">
  8. <property name="filePath"value="/tmp/prospring25"/>
  9. </bean>
  10. </beans>

3、使用关闭钩子

注入关闭代码:
  1. publicclassShutdownHookimplementsRunnable {
  2. privateConfigurableListableBeanFactory beanFactory;
  3. publicShutdownHook(ConfigurableListableBeanFactory beanFactory) {
  4. Assert.notNull(beanFactory,"The 'beanFactory' argument must not be null.");
  5. this.beanFactory = beanFactory;
  6. }
  7. publicvoidrun() {
  8. this.beanFactory.destroySingletons();
  9. }
  10. }

调用代码:
  1. publicclassShutdownHookDemo {
  2. publicstaticvoidmain(String[] args)throwsIOException {
  3. XmlBeanFactory factory =newXmlBeanFactory(newClassPathResource("/META-INF/spring/lifecycledemo5-context.xml"));
  4. Runtime.getRuntime().addShutdownHook(newThread(newShutdownHook(factory)));
  5. newBufferedInputStream(System.in).read();
  6. }
  7. }





4使用方法注入

查找方法注入:
单例比非单例快上百倍、手工使用beanFactory查找bean要比依靠spring获取实体bean快一倍


方法替换:






5使用FactoryBean接口

当你无法使用new操作符来新建类对象,但又想将他们作为Spring的bean来使用时,FactoryBean是一个完美的解决方案。

FactoryBean接口声明了3个方法:getObject、getObjectType和isSingleton。
getObject:方法获取由FactoryBean创建的对象。这是一个真正被传给其他使用FactoryBean作为协作者的bean的对象。
getObjectType: 告诉Spring你的FactoryBean将返回什么类型的对象。如果事先不知道返回什么类型,将返回null。
isSingleton:告诉Spring,正在被Spring管理的是否为单例实例。


例子:
  1. publicclassMessageDigestFactoryBeanimplementsFactoryBean, InitializingBean {
  2. privatestaticfinalString DEFAULT_ALGORITHM ="MD5";
  3. privateString algorithm = DEFAULT_ALGORITHM;
  4. privateMessageDigest messageDigest;
  5. publicObject getObject()throwsException {
  6. returnthis.messageDigest.clone();
  7. }
  8. publicClass getObjectType() {
  9. returnMessageDigest.class;
  10. }
  11. publicbooleanisSingleton() {
  12. returntrue;
  13. }
  14. publicvoidsetAlgorithm(String algorithm) {
  15. this.algorithm = algorithm;
  16. }
  17. publicvoidafterPropertiesSet()throwsException {
  18. this.messageDigest = MessageDigest.getInstance(this.algorithm);
  19. }
  20. }

配置:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="sha"class="com.apress.prospring2.ch04.factoy.MessageDigestFactoryBean">
  8. <property name="algorithm"value="SHA1"/>
  9. </bean>
  10. <bean id="md5"class="com.apress.prospring2.ch04.factoy.MessageDigestFactoryBean"/>
  11. </beans>

调用:
  1. publicclassMessageDigestDemo {
  2. publicstaticvoidmain(String[] args) {
  3. XmlBeanFactory factory =newXmlBeanFactory(
  4. newClassPathResource("/META-INF/spring/factorydemo-context.xml")
  5. );
  6. MessageDigest d1 = (MessageDigest)factory.getBean("sha");
  7. MessageDigest d2 = (MessageDigest)factory.getBean("md5");
  8. calculateDigest("Hello, world", d1);
  9. calculateDigest("Hello, world", d2);
  10. }
  11. privatestaticvoidcalculateDigest(String message, MessageDigest digest) {
  12. System.out.print("Digest using "+ digest.getAlgorithm() +": ");
  13. digest.reset();
  14. finalbyte[] bytes = digest.digest(message.getBytes());
  15. for(byteb : bytes) {
  16. System.out.print(String.format("%02x", b));
  17. }
  18. System.out.println("");
  19. }
  20. }


直接访问FactoryBean:
在调用getBean时在bean名字前面添加一个“&”作为前缀就可以获取FactoryBean。
  1. XmlBeanFactory factory =newXmlBeanFactory(
  2. newClassPathResource("/META-INF/spring/factorydemo-context.xml")
  3. );
  4. MessageDigestFactoryBean factoryBean = (MessageDigestFactoryBean)factory.getBean("&sha");
  5. MessageDigest d1 = (MessageDigest)factory.getBean("sha");
  6. MessageDigest d2 = (MessageDigest)factoryBean.getObject();
  7. System.out.println("Equal MessageDigests created? "
  8. + (d1.getAlgorithm().equals(d2.getAlgorithm())));







6BeanFactoryPostProcessor类
Spring BeanFactoryPostProcessor类 (“排队”“后”控制修改beanfactory管理的bean)

他可以获取beanfactory可以加载到的配置信息,然后可以修改这些信息,导致创建的bean得到了你期望的修改。


BeanFactoryPostProcessor的概念跟BeanPostProcessor相似:BeanFactoryPostProcessor会在Spring构建完BeanFactory之后,BeanFactory构建其他bean之前执行。
(提示:实际上,Spring可以检测到BeanFactoryPostProcessor bean并在BeanFactory初始化其他任何bean之前初始化他们)


Spring中BeanFactory的回调处理器:
AspectJWeavingEnabler:此回调处理器注册AspectJ的ClassPreProcessorAgentAdapter类并在Spring的LoadTimeWeaver中使用。
Post-processor that registers AspectJ's org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter with the Spring application context's default org.springframework.instrument.classloading.LoadTimeWeaver.

CustomAutowireConfigurer:此类可以让你指定注解,除了@Qualifier之外,标识一个bean是自动织入的志愿者。
A org.springframework.beans.factory.config.BeanFactoryPostProcessor implementation that allows for convenient registration of custom autowire qualifier types.
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>mypackage.MyQualifier</value>
</set>
</property>
</bean>

CustomEditorConfigurer:此类注册一个PropertyEditor实现,该实现用户将配置文件中的字符串值转换为bean需要的类型。
BeanFactoryPostProcessor implementation that allows for convenient registration of custom property editors.
As of Spring 2.0, the recommended usage is to use custom PropertyEditorRegistrar implementations that in turn register any desired editors on a given registry. Each PropertyEditorRegistrar can register any number of custom editors.
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="mypackage.MyCustomDateEditorRegistrar"/>
<bean class="mypackage.MyObjectEditorRegistrar"/>
</list>
</property>
</bean>
Alternative configuration example with custom editor classes:
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value="mypackage.MyCustomDateEditor"/>
<entry key="mypackage.MyObject" value="mypackage.MyObjectEditor"/>
</map>
</property>
</bean>
Also supports "java.lang.String[]"-style array class names and primitive class names (e.g. "boolean"). Delegates to ClassUtils for actual class name resolution.

CustomScopeConfigurer:使用回调处理器在配置文件中配置自定义作用域(除了单例、原型、请求、会话和全局会话之外)。赋值作用域属性到一个Map类型,他包含作用域名的键,并使用作用域接口的实现作为值。
Simple BeanFactoryPostProcessor implementation that registers custom Scope(s) with the containing ConfigurableBeanFactory.
Will register all of the supplied scopes with the ConfigurableListableBeanFactory that is passed to the postProcessBeanFactory(ConfigurableListableBeanFactory) method.
This class allows for declarative registration of custom scopes. Alternatively, consider implementing a custom BeanFactoryPostProcessor that calls ConfigurableBeanFactory.registerScope programmatically.

PreferencesPlaceholderConfigurer:此回调处理器将JDK1.4Preperences API替换bean属性中的值,此Preperences API标识他将解析来自用户Preperences的值,然后系统Preperences获得值,最后从一个引用文件获得值。
Subclass of PropertyPlaceholderConfigurer that supports JDK 1.4's Preferences API (java.util.prefs).
Tries to resolve placeholders as keys first in the user preferences, then in the system preferences, then in this configurer's properties. Thus, behaves like PropertyPlaceholderConfigurer if no corresponding preferences defined.
Supports custom paths for the system and user preferences trees. Also supports custom paths specified in placeholders ("myPath/myPlaceholderKey"). Uses the respective root node if not specified.

PropertyOverrideConfigurer:此回调处理器将替换bean属性值,而值是从指定属性文件装载的。他将从属性文件搜索使用bean名和参数构造的属性。对于bean x,他将在属性文件中查找x.a。若属性并不存在,回调处理器将不会理睬从配置文件中发现的值。
Property resource configurer that overrides bean property values in an application context definition. It pushes values from a properties file into bean definitions.
Configuration lines are expected to be of the following form:
beanName.property=value
Example properties file:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
In contrast to PropertyPlaceholderConfigurer, the original definition can have default values or no values at all for such bean properties. If an overriding properties file does not have an entry for a certain bean property, the default context definition is used.
Note that the context definition is not aware of being overridden; so this is not immediately obvious when looking at the XML definition file.
In case of multiple PropertyOverrideConfigurers that define different values for the same bean property, the last one will win (due to the overriding mechanism).
Property values can be converted after reading them in, through overriding the convertPropertyValue method. For example, encrypted values can be detected and decrypted accordingly before processing them.

PropertyPlaceholderConfigurer:此回调处理器将替换从已配置的属性文件装载的属性值。只要值符合某种格式规则,就会发生替换。
A property resource configurer that resolves placeholders in bean property values of context definitions. It pulls values from a properties file into bean definitions.
The default placeholder syntax follows the Ant / Log4J / JSP EL style:
${...}
Example XML context definition:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${driver}</value></property>
<property name="url"><value>jdbc:${dbname}</value></property>
</bean>
Example properties file:
driver=com.mysql.jdbc.Driver
dbname=mysql:mydb
PropertyPlaceholderConfigurer checks simple property values, lists, maps, props, and bean names in bean references. Furthermore, placeholder values can also cross-reference other placeholders, like:
rootPath=myrootdir
subPath=${rootPath}/subdir
In contrast to PropertyOverrideConfigurer, this configurer allows to fill in explicit placeholders in context definitions. Therefore, the original definition cannot specify any default values for such bean properties, and the placeholder properties file is supposed to contain an entry for each defined placeholder.
If a configurer cannot resolve a placeholder, a BeanDefinitionStoreException will be thrown. If you want to check against multiple properties files, specify multiple resources via the "locations" setting. You can also define multiple PropertyPlaceholderConfigurers, each with its own placeholder syntax.
Default property values can be defined via "properties", to make overriding definitions in properties files optional. A configurer will also check against system properties (e.g. "user.dir") if it cannot resolve a placeholder with any of the specified properties. This can be customized via "systemPropertiesMode".
Note that the context definition is aware of being incomplete; this is immediately obvious to users when looking at the XML definition file. Hence, placeholders have to be resolved; any desired defaults have to be defined as placeholder values as well (for example in a default properties file).
Property values can be converted after reading them in, through overriding the convertPropertyValue method. For example, encrypted values can be detected and decrypted accordingly before processing them.

ServletContextPropertyPlaceholderConfigurer:此回调处理器泛化了PropertyPlaceholderConfigurer类。因此,只要bean属性遵照指定命名规范,他就替换bean的属性。除了他的超类,此处理器还将从处理应用程序的servlet上下文参数入口装载值。
Subclass of PropertyPlaceholderConfigurer that resolves placeholders as ServletContext init parameters (that is, web.xml context-param entries).
Can be combined with "locations" and/or "properties" values in addition to web.xml context-params. Alternatively, can be defined without local properties, to resolve all placeholders as web.xml context-params (or JVM system properties).
If a placeholder could not be resolved against the provided local properties within the application, this configurer will fall back to ServletContext parameters. Can also be configured to let ServletContext init parameters override local properties (contextOverride=true).
Optionally supports searching for ServletContext attributes: If turned on, an otherwise unresolvable placeholder will matched against the corresponding ServletContext attribute, using its stringified value if found. This can be used to feed dynamic values into Spring's placeholder resolution.
If not running within a WebApplicationContext (or any other context that is able to satisfy the ServletContextAware callback), this class will behave like the default PropertyPlaceholderConfigurer. This allows for keeping ServletContextPropertyPlaceholderConfigurer definitions in test suites.




例子:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="bfpp"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  8. <property name="location"value="classpath:/META-INF/bfpp.properties"/>
  9. </bean>
  10. <bean id="simpleBean"class="com.apress.prospring2.ch04.bfpp.SimpleBean">
  11. <property name="connectionString"value="${simpleBean.connectionString}"/>
  12. <property name="password"value="${simpleBean.password}"/>
  13. <property name="username"value="username"/>
  14. </bean>
  15. </beans>

调用获取BeanFactoryPostProcessor:
下面的获取可以让你明白什么情况下也使用了BeanFactoryPostProcessor,或者BeanFactoryPostProcessor的作用。
  1. ConfigurableListableBeanFactory beanFactory =newXmlBeanFactory(
  2. newClassPathResource("/META-INF/spring/bfpp2-context.xml")
  3. );
  4. BeanFactoryPostProcessor bfpp = (BeanFactoryPostProcessor)beanFactory.getBean("bfpp");
  5. bfpp.postProcessBeanFactory(beanFactory);
  6. System.out.println(beanFactory.getBean("simpleBean"));



编写一个BeanFactoryPostProcessor:
  1. packagecn.partner4java.beanfactorypostprocessor;
  2. importjava.util.HashSet;
  3. importjava.util.Set;
  4. importorg.springframework.beans.BeansException;
  5. importorg.springframework.beans.factory.BeanNameAware;
  6. importorg.springframework.beans.factory.config.BeanDefinition;
  7. importorg.springframework.beans.factory.config.BeanDefinitionVisitor;
  8. importorg.springframework.beans.factory.config.BeanFactoryPostProcessor;
  9. importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory;
  10. importorg.springframework.util.StringValueResolver;
  11. publicclassObscenityRemovingBeanFactoryPostProcessorimplements
  12. BeanFactoryPostProcessor, BeanNameAware {
  13. privateString name;
  14. privateSet<String> obscenities;
  15. privateSet<String> obscenitiesRemoved;
  16. publicObscenityRemovingBeanFactoryPostProcessor() {
  17. this.obscenities =newHashSet<String>();
  18. this.obscenitiesRemoved =newHashSet<String>();
  19. }
  20. publicvoidpostProcessBeanFactory(
  21. ConfigurableListableBeanFactory beanFactory)throwsBeansException {
  22. String[] beanNames = beanFactory.getBeanDefinitionNames();
  23. //下面两个类是用来替换参数的
  24. //Simple strategy interface for resolving a String value.
  25. StringValueResolver valueResolver =newStringValueResolver() {
  26. publicString resolveStringValue(String strVal) {
  27. if(isObscene(strVal)){
  28. obscenitiesRemoved.add(strVal);
  29. return"****";
  30. }
  31. returnstrVal;
  32. }
  33. };
  34. //Visitor class for traversing BeanDefinition objects, in particular the property values and constructor argument values contained in them, resolving bean metadata values.
  35. //Used by PropertyPlaceholderConfigurer to parse all String values contained in a BeanDefinition, resolving any placeholders found.
  36. BeanDefinitionVisitor visitor =newBeanDefinitionVisitor(valueResolver);
  37. for(String beanName:beanNames){
  38. if(beanName.equals(this.name))continue;
  39. //如果不是实体就应该是参数,获取参数
  40. BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
  41. visitor.visitBeanDefinition(beanDefinition);
  42. }
  43. beanFactory.registerSingleton("obscenitiesRemoved",this.obscenitiesRemoved);
  44. }
  45. privatebooleanisObscene(Object value) {
  46. String potentialObscenity = value.toString().toUpperCase();
  47. returnthis.obscenities.contains(potentialObscenity);
  48. }
  49. publicvoidsetObscenities(Set<String> obscenities) {
  50. this.obscenities.clear();
  51. for(String obscentity:obscenities){
  52. this.obscenities.add(obscentity.toUpperCase());
  53. }
  54. }
  55. publicvoidsetBeanName(String name) {
  56. this.name = name;
  57. }
  58. }

配置文件:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="bfpp"class="cn.partner4java.beanfactorypostprocessor.ObscenityRemovingBeanFactoryPostProcessor">
  8. <property name="obscenities">
  9. <set>
  10. <value>winky</value>
  11. <value>bum</value>
  12. </set>
  13. </property>
  14. </bean>
  15. <bean id="simpleBean"class="cn.partner4java.beanfactorypostprocessor.SimpleBean">
  16. <property name="connectionString"value="bollocks"/>
  17. <property name="password"value="winky"/>
  18. <property name="username"value="bum"/>
  19. </bean>
  20. </beans>

调用:
  1. ConfigurableListableBeanFactory beanFactory =newXmlBeanFactory(
  2. newClassPathResource("/META-INF/spring/spring-beanfactorypostprocessor.xml")
  3. );
  4. BeanFactoryPostProcessor bfpp = (BeanFactoryPostProcessor)beanFactory.getBean("bfpp");
  5. bfpp.postProcessBeanFactory(beanFactory);
  6. SimpleBean simpleBean = (SimpleBean) beanFactory.getBean("simpleBean");
  7. System.out.println(simpleBean);
  8. System.out.println(beanFactory.getBean("obscenitiesRemoved"));

后台打印:
SimpleBean{password='****', username='****', connectionString='bollocks'}
[bum, winky]










7JavaBean的属性修改器
Spring使用内建的PropertyEditor将String样式的各个属性转换成正确的类型。

Spring中的PropertyEditor实现类:
ByteArrayPropertyEditor:PropertyEditor将String值转换为byte数组。

ClassEditor:ClassEditor将全命名的类名称转换为类实例。使用此PropertyEditor时,注意不要在全类名两边包含不相关的空格。

CustomBooleanEditor:此自定义editor用于boolean值,处理以UI为中心的代码,他能解析Boolean值的不同String表示。

CustomCollectionEditor:此PropertyEditor用于创建Java集合框架或者数组的任意类型。

CustomDateEditor:与CustomBooleanEditor类相似,与PropertyEditor实现类用于控制器的initBinder方法,使应用程序可以解析出入的制定格式的日期为java.util.Date.

CustomNumberEditor:PropertyEditor实现类将字符串转换为Integer、Long、BigDecimal或者其他任意数据类型子类。

FileEditor:FileEditor将字符串文件路径转换为File实例,Spring不会检测文件或者目录是否存在。

InputStreamEditor:此为一个InputStream对象。注意PropertyEditor是你了反射的。他只能转换字符串为InputStream对象。在内部,通过为Resource实例化一个临时的ResourceEditor来完成转换。

LocaleEditor:LocalEditor实现将表示区域的字符串(如en_GB)转换为一个java.util.Locale实例。

PatternEditor:ResourceEditor转换正规表达式为一个java.util.regex.Pattern实例。

PropertiesEditor:PropertiesEditor类转换键值对形式的字符串,key1=hello为java.util.Properties,并使用相应的属性配置。

StringArrayPropertyEditor:StringArrayPropertyEditor类转换逗号为分割字符串元素列表为字符串数组。

StringTrimmerEditor:用于删除字符串两边的空格,并转换每一个字符串元素为null。
Property editor that trims Strings.
Optionally allows transforming an empty string into a null value. Needs to be explictly registered, e.g. for command binding.

URLEditor:将URL字符串表示转换为java.net.URL实例。


(这些属性修改器都是可以手工编写定制的)






8BeanPostProcessor类

在Spring实例化bean的前后执行一些附加操作。

有时,你会发现需要立刻在Spring实例化一个bean的前后执行一些附件操作。这些操作可以简单到修改一个bean,也可以复杂到返回一个完全不同的对象。
BeanPostProcessor接口包含两个方法:
postProcessBeforeInitialization:在Spring调用任何bean的初始化钩子(例如InitializingBean.afterPropertiesSet或者init方法)之前被调用。
postProcessAfterInitialization:Spring在成功完成嵌入初始化以后调用他。

例子1:
我们声明了一个实现了BeanPostProcessor接口的类InitDestroyAnnotationBeanPostProcessor,这个类支持声明式的指定注解规则(默认为JSR 250注解)
  1. publicclassSimpleBean {
  2. @PostConstruct
  3. publicvoidinitialize() {
  4. System.out.println("Initializing bean "+ getClass());
  5. }
  6. @PreDestroy
  7. publicvoidcleanUp() {
  8. System.out.println("Cleaning up bean "+ getClass());
  9. }
  10. }


配置:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="simpleBean"class="com.apress.prospring2.ch04.bpp.SimpleBean"/>
  8. <bean id="bpp"class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor">
  9. <property name="initAnnotationType"value="javax.annotation.PostConstruct"/>
  10. <property name="destroyAnnotationType"value="javax.annotation.PreDestroy"/>
  11. </bean>
  12. </beans>

调用:
  1. publicclassSimpleBeanDemo {
  2. publicstaticvoidmain(String[] args) {
  3. ConfigurableListableBeanFactory beanFactory =newXmlBeanFactory(
  4. newClassPathResource("/META-INF/spring/bpp-context.xml")
  5. );
  6. registerPostProcessor(beanFactory,"bpp");
  7. SimpleBean sb = (SimpleBean)beanFactory.getBean("simpleBean");
  8. System.out.println(sb);
  9. beanFactory.destroySingletons();
  10. }
  11. privatestaticvoidregisterPostProcessor(ConfigurableListableBeanFactory beanFactory, String beanName) {
  12. BeanPostProcessor bpp = (BeanPostProcessor) beanFactory.getBean(beanName);
  13. beanFactory.addBeanPostProcessor(bpp);
  14. }
  15. }





例子2:自己实现一个BeanPostProcessor接口(改变bean的一个字段数据)
(例子的实现是像上面的例子一样,自己定义一个时间注解的实现)
  1. packagecn.partner4java.beanpostprocessor;
  2. importjava.lang.annotation.ElementType;
  3. importjava.lang.annotation.Retention;
  4. importjava.lang.annotation.RetentionPolicy;
  5. importjava.lang.annotation.Target;
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.FIELD)
  8. public@interfaceTimeStamp {
  9. }
  1. packagecn.partner4java.beanpostprocessor;
  2. importjava.util.Date;
  3. importjavax.annotation.PostConstruct;
  4. importjavax.annotation.PreDestroy;
  5. publicclassSimpleBean {
  6. //自定义的注解
  7. @TimeStamp
  8. Date createDate;
  9. //spring中InitDestroyAnnotationBeanPostProcessor实现对bean init替代的注解
  10. @PostConstruct
  11. publicvoidinitialize(){
  12. System.out.println("initialize: "+ getClass());
  13. }
  14. //spring中InitDestroyAnnotationBeanPostProcessor实现对bean destory替代的注解
  15. @PreDestroy
  16. publicvoidcleanup(){
  17. System.out.println("cleanup: "+ getClass());
  18. }
  19. @Override
  20. publicString toString() {
  21. return"SimpleBean [createDate="+ createDate +"]";
  22. }
  23. }



编写一个BeanPostProcessor来检查每一个bean并为他们注解了日志字段设置当前日期:
  1. packagecn.partner4java.beanpostprocessor;
  2. importjava.lang.reflect.Field;
  3. importjava.util.Date;
  4. importorg.springframework.beans.BeansException;
  5. importorg.springframework.beans.factory.config.BeanPostProcessor;
  6. importorg.springframework.util.ReflectionUtils;
  7. publicclassTimestampingBeanPostProcessorimplementsBeanPostProcessor {
  8. publicObject postProcessBeforeInitialization(finalObject bean, String beanName)
  9. throwsBeansException {
  10. ReflectionUtils.doWithFields(bean.getClass(),
  11. newReflectionUtils.FieldCallback() {
  12. publicvoiddoWith(Field field)throwsIllegalArgumentException,
  13. IllegalAccessException {
  14. field.set(bean,newDate());
  15. }
  16. },newReflectionUtils.FieldFilter() {
  17. publicbooleanmatches(Field field) {
  18. returnfield.getType() == Date.class&& field.getAnnotation(TimeStamp.class) !=null;
  19. }
  20. });
  21. returnbean;
  22. }
  23. publicObject postProcessAfterInitialization(Object bean, String beanName)
  24. throwsBeansException {
  25. returnbean;
  26. }
  27. }

配置文件:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="simpleBean"class="cn.partner4java.beanpostprocessor.SimpleBean"/>
  8. <bean id="bpp1"class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor">
  9. <property name="initAnnotationType"value="javax.annotation.PostConstruct"/>
  10. <property name="destroyAnnotationType"value="javax.annotation.PreDestroy"/>
  11. </bean>
  12. <bean id="bpp2"class="cn.partner4java.beanpostprocessor.TimestampingBeanPostProcessor"/>
  13. </beans>

调用:
  1. packagecn.partner4java.beanpostprocessor;
  2. importjavax.annotation.PreDestroy;
  3. importorg.springframework.beans.factory.config.BeanPostProcessor;
  4. importorg.springframework.beans.factory.config.ConfigurableListableBeanFactory;
  5. importorg.springframework.beans.factory.xml.XmlBeanFactory;
  6. importorg.springframework.core.io.ClassPathResource;
  7. publicclassSimpleBeanDemo {
  8. publicstaticvoidmain(String[] args) {
  9. ConfigurableListableBeanFactory beanFactory =newXmlBeanFactory(
  10. newClassPathResource("/META-INF/spring/spring-beanpostprocessor.xml")
  11. );
  12. registerPostProcessor(beanFactory,"bpp1");
  13. registerPostProcessor(beanFactory,"bpp2");
  14. SimpleBean sb = (SimpleBean)beanFactory.getBean("simpleBean");
  15. System.out.println(sb);
  16. //@PreDestroy是被这个方法调用执行的
  17. beanFactory.destroySingletons();
  18. }
  19. privatestaticvoidregisterPostProcessor(ConfigurableListableBeanFactory beanFactory, String beanName) {
  20. BeanPostProcessor bpp = (BeanPostProcessor) beanFactory.getBean(beanName);
  21. beanFactory.addBeanPostProcessor(bpp);
  22. }
  23. }
  24. //后台打印:
  25. //initialize: class cn.partner4java.beanpostprocessor.SimpleBean
  26. //SimpleBean [createDate=Tue Nov 15 16:18:33 CST 2011]
  27. //cleanup: class cn.partner4java.beanpostprocessor.SimpleBean





例子3:
Spring自带的简单支持,例子中完全修改了一个bean的类型,从Dependency类转变为了String类型的字符串。(改变一个bean的类型)
(org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor)
(继承public abstract class InstantiationAwareBeanPostProcessorAdapter implements SmartInstantiationAwareBeanPostProcessor。
这让我们使用这个回调处理适配器来实现能够预测回调处理bean的类型的BeanPostProcessor接口)

我们在下面的定义中指定了一个自动关联的注解,但是我们系统中并没有String类型的dependency关联,只要一个Dependency类交给了Spring。
  1. publicclassSimpleBean {
  2. @Timestamp
  3. Date creationDate;
  4. @Autowired
  5. String dependency;
  6. @PostConstruct
  7. publicvoidinitialize() {
  8. System.out.println("Initializing bean "+ getClass());
  9. }
  10. @PreDestroy
  11. publicvoidcleanUp() {
  12. System.out.println("Cleaning up bean "+ getClass());
  13. }
  14. @Override
  15. publicString toString() {
  16. return"Bean was created at "+this.creationDate +" with "+this.dependency;
  17. }
  18. }


我们修改bean的类型:
  1. publicclassTypedDependencyBeanPostProcessorextendsInstantiationAwareBeanPostProcessorAdapter {
  2. publicClass predictBeanType(Class beanClass, String beanName) {
  3. if(beanClass.equals(Dependency.class)) {
  4. returnString.class;
  5. }
  6. returnbeanClass;
  7. }
  8. publicObject postProcessBeforeInitialization(finalObject bean,finalString beanName)throwsBeansException {
  9. if(bean.getClass().equals(Dependency.class)) {
  10. return"Hello, world";
  11. }
  12. returnbean;
  13. }
  14. publicObject postProcessAfterInitialization(Object bean, String beanName)throwsBeansException {
  15. returnbean;
  16. }
  17. }


配置:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="dependency"class="com.apress.prospring2.ch04.bpp.Dependency"/>
  8. <bean id="simpleBean"class="com.apress.prospring2.ch04.bpp.SimpleBean"/>
  9. <bean id="bpp"class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor">
  10. <property name="initAnnotationType"value="javax.annotation.PostConstruct"/>
  11. <property name="destroyAnnotationType"value="javax.annotation.PreDestroy"/>
  12. </bean>
  13. <bean id="bpp2"class="com.apress.prospring2.ch04.bpp.TimestampingBeanPostProcessor"/>
  14. <bean id="bpp3"class="com.apress.prospring2.ch04.bpp.TypedDependencyBeanPostProcessor"/>
  15. <bean id="bpp4"class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
  16. </beans>









9Spring ApplicationContext

ApplicationContext是对BeanFactory的扩展:他提供相同的功能,但却减少了与之交互所需编写的代码,并且还新增了额外的功能。
ApplicationContext的主要功能是为构建你的应用程序提供一个更加丰富的框架。
ApplicationContext最大的好处在与他允许你完全声明性的方法配置和管理Spring以及Spring下的资源。
国际化
事件发布
资源的管理和访问
增加的生命周期接口
改进了的基础设施组件自动化配置




ApplicationContext的实现类:
FileSystemXmlApplicationContext:可以在文件系统中应用程序有权访问的任何位置加载。
ClasspathXmlApplicationContext:可以从classpath上任何位置加载配置,这在你需要将配置文件跟一些类文件一起打包在一个JAR文件内时会很有用处。
XmlWebApplicationContext:是专门在Web应用环境中使用的,通过使用ContextLoaderListener或者ContextLoaderServlet,你可以为你的Web应用自动加载ApplicationContext配置。




使用ApplicationContextAware:
一个bean可以实现BeanFactoryAware接口的方式获得一个他所在BeanFactory的引用。同样,也可以通过实现ApplicationContextAware接口获得他在ApplicationContext的引用。
  1. publicclassContextAwareDemoimplementsApplicationContextAware {
  2. privateApplicationContext ctx;
  3. publicvoidsetApplicationContext(ApplicationContext applicationContext)
  4. throwsBeansException {
  5. ctx = applicationContext;
  6. }
  7. publicstaticvoidmain(String[] args) {
  8. ApplicationContext ctx =newClassPathXmlApplicationContext(
  9. "/META-INF/spring/acdemo1-context.xml");
  10. ContextAwareDemo demo = (ContextAwareDemo) ctx.getBean("contextAware");
  11. demo.displayAppContext();
  12. }
  13. publicvoiddisplayAppContext() {
  14. System.out.println(ctx);
  15. }
  16. }

配置:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="contextAware"class="com.apress.prospring2.ch04.context.ContextAwareDemo"/>
  8. </beans>






控制bean的初始化:
<bean .. lazy-init="false" ../>



使用基于注解的配置:
@Component :基本类型,使用注解的类将成为Spring的bean
@Controller :作为一个Spring MVC支持的控制器(带有这个注解的类会成为Spring MVC控制器)
@Repository :表示一个库,如一个数据库访问对象(将获得自动异常转换)
@Service :标记实现应用程序部分业务逻辑的类

指明要扫描注解的路径:
<context:component-scan base-package="com.apress.prospring2.ch04.annotations"/>


Spring中过滤器的类型:
annotation:设置另一个注解类型,你讲使用他表示你希望Spring扫描的类。
assignable:为应该赋值的类指定完整的类名。
regex:为应该匹配的类指定正则表达式。
aspectj:使用横切点类型的表达式指定锁使用类的全名。

demo:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="
  6. http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd">
  10. <context:component-scan base-package="com.apress.prospring2.ch04.annotations">
  11. <context:include-filter type="annotation"expression="com.apress.prospring2.ch04.annotations.Magic"/>
  12. <context:include-filter type="assignable"expression="com.apress.prospring2.ch04.annotations.ComponentMarker"/>
  13. <context:include-filter type="aspectj"expression="* void com.apress..annotations.*Service*(..)"/>
  14. </context:component-scan>
  15. </beans>









使用MessageSource进行国际化:
123-130页





使用应用程序事件:
ApplicationContext还可以作为中间人发布和接受事件。

例子:
event
  1. publicclassMessageEventextendsApplicationEvent {
  2. privatestaticfinallongserialVersionUID = -6786033091498612636L;
  3. privateString message;
  4. publicMessageEvent(Object source, String message) {
  5. super(source);
  6. this.message = message;
  7. }
  8. publicString getMessage() {
  9. returnmessage;
  10. }
  11. }

Listener:
  1. publicclassMessageEventListenerimplementsApplicationListener {
  2. publicvoidonApplicationEvent(ApplicationEvent event) {
  3. if(eventinstanceofMessageEvent) {
  4. MessageEvent messageEvent = (MessageEvent)event;
  5. System.out.println("Received "+ messageEvent.getMessage() +
  6. " from "+ messageEvent.getSource());
  7. }
  8. }
  9. }

调用:
  1. publicclassEventPublisherDemoimplementsApplicationContextAware {
  2. privateApplicationContext ctx;
  3. publicstaticvoidmain(String[] args) {
  4. ApplicationContext ctx =newClassPathXmlApplicationContext(
  5. "/META-INF/spring/eventsdemo1-context.xml");
  6. EventPublisherDemo pub = (EventPublisherDemo) ctx.getBean("publisher");
  7. pub.publish("Hello World!");
  8. pub.publish("The quick brown fox jumped over the lazy dog");
  9. }
  10. publicvoidsetApplicationContext(ApplicationContext applicationContext)
  11. throwsBeansException {
  12. this.ctx = applicationContext;
  13. }
  14. publicvoidpublish(String message) {
  15. ctx.publishEvent(newMessageEvent(this, message));
  16. }
  17. }

配置:
  1. <?xml version="1.0"encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd">
  7. <bean id="publisher"class="com.apress.prospring2.ch04.event.EventPublisherDemo"/>
  8. <beanclass="com.apress.prospring2.ch04.event.MessageEventListener"/>
  9. </beans>

(我们并不需要特殊的配置将MessageEventListener注册到ApplicationContext上,他是被Spring自动挑选出来的)








访问资源:
不管一个文件是保存在文件系统上、classpath上或者远程服务器上,你的应用程序都能以相同的方式访问到他。

核心是Resource接口。
  1. publicclassResourceDemo {
  2. publicstaticvoidmain(String[] args)throwsException{
  3. ApplicationContext ctx =newClassPathXmlApplicationContext(
  4. "/META-INF/spring/resourcesdemo1-context.xml");
  5. Resource res1 = ctx.getResource("file:///tmp");
  6. displayInfo(res1);
  7. Resource res2 = ctx.getResource("classpath:com/apress/prospring2/ch04/pe/Complex.class");
  8. displayInfo(res2);
  9. Resource res3 = ctx.getResource("http://www.google.co.uk");
  10. displayInfo(res3);
  11. }
  12. privatestaticvoiddisplayInfo(Resource res)throwsException{
  13. System.out.println(res.getClass());
  14. System.out.println(res.getURL().getContent());
  15. System.out.println("");
  16. }
  17. }