springIOC容器 高级部分

1、spring ioc容器

spring提供两种容器的实现,基本的成为bean 工厂(bean Factory),更高级的称为应用程序上下文(Aplication Context)。这两种容器的配置文件相同。Bean工厂和应用程序上下文的接口分别是BeanFactoryApplication Context。Application Context是用于保证兼容性BeanFactory的子类。
Application Context仅仅是一个接口,ClassPathXmlApplicationContext实现从classpath中装入一个xml配置文件,构建一个应用程序上下文。
Application Context的其它实现:

  • FileSystemXmlApplicationContext 用于从文件系统或者是从URL中装载xml配置文件
  • XmlWebApplicationContext和XmlPorltletApplication:仅能用于Web和入口程序

1.1 使用Spring的Factory Bean创建bean

Spring bean容器中有两种bean:普通bean和工厂bean。Spring直接使用前者,而后者自身可以生成由框架管理的对象。
简单地说,我们可以通过实现org.springframework.beans.factory.FactoryBean接口来构建一个工厂bean 。
工厂Bean是作为ioc容器中其它bean的工厂的一个bean。工厂Bean主要用于实现框架机制。

补记

  1. bean工厂:不是bean,在spring中一般指的是DefaultListableBeanFactory对象,管理和维护spring中所有的bean
  2. 工厂bean:一种特殊的bean,在xml文件中配置的,用来生成新的bean的加工厂,通过getObject()方法可以获取其生产的新bean,如果想获取该工厂bean本身,需要使用类似于getBean("&" + beanName)的样式。
public class ColleageFactory extends AbstractFactoryBean<Colleage>{

	private String colleageName;
	
	private String location;
	
	public String getColleageName() {
		return colleageName;
	}

	public void setColleageName(String colleageName) {
		this.colleageName = colleageName;
	}

	public String getLocation() {
		return location;
	}

	public void setLocation(String location) {
		this.location = location;
	}
	
	@Override
	protected Colleage createInstance() throws Exception {
		Colleage colleage = new Colleage();
		colleage.setColleageName(colleageName);
		colleage.setLocation(location);
		return colleage;
	}

	@Override
	public Class<?> getObjectType() {
		return Colleage.class;
	}
}

虽然Spring容器使用FactoryBean的getObject()方法的返回值作为bean,但也可以使用FactoryBean本身。
要访问FactoryBean,您只需要在bean名称之前添加一个“&”。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:beans.xml" })
public class TestFactory {
	@Resource(name="&colleage")
	private ColleageFactory colleageFactory;
	@Test
	public void testColleageFactory2() {
		System.out.println(colleageFactory.getColleageName());
		System.out.println(colleageFactory.getLocation());
	}
}

1.2 创建单例或原型对象的工厂bean。

重写这个方法

	/**
	 * Set if a singleton should be created, or a new object on each request
	 * otherwise. Default is <code>true</code> (a singleton).
	 */
	public void setSingleton(boolean singleton) {
		this.singleton = singleton;
	}

2 Spring Ioc容器高级

2.1 调用静态工厂方法创建Bean

Spring 支持静态工厂方法创建bean,这个属性在factory-method中指定。
声明一个静态工厂方法创建的bean,需要在class属性中指定工厂方法的宿主类。在factory-method属性中指定工厂方法的名称。最后,使用传递参数。

/**
 * @author zhouzixiang
 *静态工厂方法创建bean
 */
public class ProductCreator {
	public static Product createProduct (String productName) {
		if ("battery".equals(productName)) {
			return new Battery("电池","南孚电池");
		} else if ("disc".equals(productName)){
			return new Disc("CD","滚石唱片");
		}
		throw new IllegalArgumentException("参数不合法");
	}
}
		 <bean id="battery" class="com.pc.staticFactory.ProductCreator" factory-method="createProduct">
		 	<constructor-arg name="productName" value="battery"></constructor-arg>
    	</bean>
    	
    	 <bean id="disc" class="com.pc.staticFactory.ProductCreator" factory-method="createProduct">
		 	<constructor-arg name="productName" value="disc"></constructor-arg>
    	</bean>

2.2 实例方法创建Bean

	<!-- 实例工厂 -->
	<bean id="initialFactory" class="com.pc.initialFactory.InitialFactory">
		<property name="products">
			<map>
				<entry key="battery" >
					<bean class="com.pc.entity.Battery">
						<property name="productName" value="南孚电池"></property>
						<property name="description" value="一节更比6节强"></property>
					</bean>
				</entry>
				<entry key="disc">
					<bean class="com.pc.entity.Disc">
						<property name="productName" value="滚石"></property>
						<property name="description" value="roll stock"></property>
					</bean>
				</entry>
			</map>
		</property>
	</bean>
	<!-- 使用实例工厂创建bean  -->
	<bean id="battery" factory-bean="initialFactory" factory-method="createProduct">
		<constructor-arg name="productName" value="battery"></constructor-arg>
	</bean>
	
	<bean id="disc" factory-bean="initialFactory" factory-method="createProduct">
		<constructor-arg name="productName" value="disc"></constructor-arg>
	</bean>

2.3 其它声明bean的方式

除了上面介绍的两种方法外,还有从静态字段中声明bean,从对象属性中声明bean。用的比较少,不做介绍了。

2.4 使Bean感知容器

一个精心设计的组件应该没有对容器的直接依赖,但是有时候Bean有必要了解容器的资源。
自定义的Bean通过实现某些“感知”接口来了解容器资源,Spring通过这些接口中定义的设置方法将对应的资源注入到你的Bean中。
Spring中常见的感知接口:

  1. BeanNameAware 可以在Bean中得到它在IOC容器中的Bean的实例名称

  2. BeanFactoryAware:可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务

  3. ApplicationContextAware:可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务

  4. MessageSourceAware:在Bean中可以得到消息源,可以通过它解析文本消息

  5. ApplicationEventPublisherAware:在Bean中可以得到应用上下文的事件发布器,通过它你可以发布应用事件

  6. ResourceLoaderAware :资源装载器,通过它加载外部资源。

实际上,ApplicationContext接口扩展了MessageSource、ApplicationEventPublisher、ResourceLoader等接口,所以你只要知道了应用上下文就可以访问所有的服务,但是最佳的做法是满足最小作用域的感知接口。

示例:

/**
 * @author zzx
 *使Cashier bean对象能够感知容器的存在
 */
public class Cashier implements BeanNameAware {

	/**
	 * 收银员name
	 */
	private String name;
	@Override
	public void setBeanName(String beanName) {
		this.name = beanName;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}
<!-- 可感知容器的bean -->
	<!-- 该bean把容器中的bean名称作为收银员的名称  可以看到没有进行name属性注入 也打印出了 张翠花-->
	<bean id="张翠花" class="com.pc.AwareBean.Cashier">
	</bean>

测试:

<!-- 可感知容器的bean -->
	<!-- 该bean把容器中的bean名称作为收银员的名称  可以看到没有进行name属性注入 也打印出了 张翠花-->
	<bean id="张翠花" class="com.pc.AwareBean.Cashier">
	</bean>

在这里插入图片描述

2.5 加载外部资源

Spring的资源装载器提供统一的getResource()方法,按照资源路径读取外部资源。

  • file前缀:从文件系统加载资源
  • classpath前缀:从classpath加载资源
  • URL :从网络中加载
当你的资源位于classpath中时,必须使用claspath前缀。如果资源路径
中没有没有前缀,这个资源将根据应用上下文从一个位置中加载,对于FileSystemXmlApplication,资源将从文件系统中加载。对于ClassPathXmlApplicationContext,将从classpath中加载。

示例代码:

/**
 * @author zzx
 * 从类路径下加载外部资源
 */
public class BannerLoader implements ResourceLoaderAware{

	private ResourceLoader resourceLoader;
 
	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader ;
	}

	public void showBanner () throws IOException {
		Resource resource = resourceLoader.getResource("classpath:banner.txt");
		InputStream inputStream = resource.getInputStream();
		InputStreamReader in = new InputStreamReader(inputStream);
		BufferedReader reader  = new BufferedReader(in);
		while (true) {
			String readLine = reader.readLine();
			if (readLine == null)
				break;
			System.out.println(readLine);
		}
		reader.close();
	}
}
<!-- 读取外部资源-->
	<bean id="bannerLoader" class="com.pc.ResourceLoader.BannerLoader" init-method="showBanner"></bean>

2.5.2 注入资源(设值注入)

除了调用getResource()方法显示加载资源,还可以使用设值方法注入资源(不再需要实现ResourceLoaderAware接口,推荐使用这种方式:

public class BannerLoader  { 
//	方式二 值注入开始
	private Resource resource;
	public void setResource(Resource resource) {
		this.resource = resource;
	}
	public void showBanner () throws IOException {
	//...
	}
}
<!-- 在bean配置中,简单指定Resource属性的资源路径。Spring将使用预先注册的属性编辑器ResourceEditor将这个属性转换为一个Resource对象,然后注入到我们的bean中-->
	<bean id="bannerLoader" class="com.pc.ResourceLoader.BannerLoader" init-method="showBanner">
		<property name="resource" value="classpath:banner.txt">
		</property>
	</bean>

2.6 创建Bean后处理器

使用场景:在spring ioc容器中注册自己的插件,在构造器件处理Bean实例。
Bean处理器允许在初始化回调方法前后进行附加的bean处理。Bean后处理器的主要特性是逐个处理IOC容器中的所有bean实例,而不是单个bean实例。一般Bean处理器用于检查Bean属性有效性,或者根据特殊条件修改Bean属性。

使用方式:实现BeanPostProcessor接口
示例参考:
https://blog.csdn.net/kuailefangyuan/article/details/17791523

2.7 外部化Bean配置

在配置文件中配置Bean 时,你必须记住,将部署细节如文件路径,服务器地址,用户名称和密码与Bean配置混在一起是不好的做法。通常Bean的配置由开发人员编写,而部署细节因不同的环境而不同,如果开发环境和测试环境以及预发布环境和线上环境等。 如何解决不同环境不同导致的重复修改Bean配置呢? Spring 有一个名称为Proper
在配置文件中配置Bean 时,你必须记住,将部署细节如文件路径,服务器地址,用户名称和密码与Bean配置混在一起是不好的做法。通常Bean的配置由开发人员编写,而部署细节因不同的环境而不同,如果开发环境和测试环境以及预发布环境和线上环境等。

 如何解决不同环境不同导致的重复修改Bean配置呢?

  Spring 有 一个名称为PropertyPlaceHolderConfigurer的Bean工厂后处理器,用来将部分Bean配置外部化为一个属性文件。你可以在 Bean的配置文件中使用${var}形式的变量,PropertyPlaceHolderConfigurer会从属性文件中加载属性并替换他们。

2.7.1 PropertyPlaceHolderConfigurer的注册

 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    	<property name="locations">
            <list>
                <value>classpath:sysConfig.properties</value>                
                <value>file:/data/pc-config/passport.properties</value>
            </list>
        </property>
        <property name="ignoreResourceNotFound"  value="true"/>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="valueSeparator"  value=":=" />
    </bean>

2.7.2 PropertyPlaceHolderConfigurer的简单注册

<context:property-placeholder/>

2.8 解析文本消息

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.8.2 补充 资源数ResourceBundle的使用

1.属性文件:外部化配置bean (beanName = class信息)
属性文件定义
2、利用工厂模式生成bean

/**
 * @author EvanZhou
 * 将class信息配置在属性文件中 ,实现工厂模式。
 */
public class BeanFactory {
	private static ResourceBundle bundle;
	static{
//		instance.properties属性文件必须放在src目录 下,或者自定义的classPath目录下。
		bundle=ResourceBundle.getBundle("instance");
	}
	
	public static <T>T getInstance(String key,Class<T>clazz){
		String className=bundle.getString(key);
		try {
			return(T)Class.forName(className).newInstance();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new RuntimeException(e);
		}
	}
	
	public static void main(String[] args) {
		Book book = getInstance("book", Book.class);
		System.out.println(book.toString());
	}
}

除此之外ResourceBundle还长用作国际资源化:
核心api:

//Locale 对象表示了特定的地理、政治和文化地区。需要 Locale 来执行其任务的操作称为语言环境敏感的 操作,它使用 Locale 为用户量身定制信息。例如,显示一个数值就是语言环境敏感的操作,应该根据用户的国家、地区或文化的风俗/传统来格式化该数值。
//使用此类中的构造方法来创建 Locale:
 //Locale(String language)
 //Locale(String language, String country)
// Locale(String language, String country, String variant)
  public static final ResourceBundle getBundle(String baseName,
    						 Locale locale)

在这里插入图片描述

2.9 组件间使用事件进行通信

Spring为什么要支持基于事件的通信?
在组件的典型通信模式中,发送者和接受者紧密耦合。使用ioc容器,你的组件通过接口而不是实现进行通信,这种模式减少了耦合。
参考代码:
https://www.xuebuyuan.com/848519.html?mobile=1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值