1、spring ioc容器
spring提供两种容器的实现,基本的成为bean 工厂(bean Factory),更高级的称为应用程序上下文(Aplication Context)。这两种容器的配置文件相同。Bean工厂和应用程序上下文的接口分别是BeanFactory 和 Application 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主要用于实现框架机制。
补记:
- bean工厂:不是bean,在spring中一般指的是DefaultListableBeanFactory对象,管理和维护spring中所有的bean
- 工厂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中常见的感知接口:
-
BeanNameAware 可以在Bean中得到它在IOC容器中的Bean的实例名称
-
BeanFactoryAware:可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务
-
ApplicationContextAware:可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务
-
MessageSourceAware:在Bean中可以得到消息源,可以通过它解析文本消息
-
ApplicationEventPublisherAware:在Bean中可以得到应用上下文的事件发布器,通过它你可以发布应用事件
-
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