精通Spring+4.x++企业开发与实践之IoC容器中装配Bean

Spring配置概述

Spring属性注入

JavaBean关于属性命名的特殊规范

Spring配置文件中的<property></property>元素所指定的属性名和Bean实现类的Setter方法知足Sun JavaBean的属性命名规范:xxx的属性对应的setXxx()方法。 注意:须要知足:"变量的前两个字母要全大写,要么全小写"。不然会出现具备误导性的错误。java

构造函数注入

1.须要提供一个有参构造函数,能够按类型匹配注入,按因此匹配注入,联合类型和因此匹配注入,经过自身类型反射匹配注入 2.构造函数注入的问题,可能出现循环依赖的问题,致使程序没法启动,只要改为属性注入便可解决这个问题。 例子: ConstructorInject.javaweb

public class ConstructorInject {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		Person person = (Person) ctx.getBean("p2");
		System.out.println(person.toString());
	}
}

iocdemo.xmlspring

<?xml version="1.0" encoding="UTF-8" ?>
<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-4.0.xsd">
<bean id="person" class="com.flexible.beans.Person">
	<property name="userName"><value>zhangsan</value></property>
	<property name="userAge"><value>20</value></property>
</bean>

	<bean id="p2" class="com.flexible.beans.Person">
		<constructor-arg type="java.lang.String"><value>zhangsan</value></constructor-arg>
		<constructor-arg type="java.lang.Integer"><value>28</value></constructor-arg>
	</bean>
</beans>

Person.javaexpress

public class Person {
public String userName;
public Integer userAge;

	public Person() {
	}

	public Person(String userName, Integer userAge) {
		this.userName = userName;
		this.userAge = userAge;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Integer getUserAge() {
		return userAge;
	}

	public void setUserAge(Integer userAge) {
		this.userAge = userAge;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public String toString() {
		return "Person{" +
				"userName='" + userName + '\'' +
				", userAge=" + userAge +
				'}';
	}
}

工厂方法注入

1.非静态的工厂
xml配置以下:
		<bean id="factory" class="com.flexible.factorymethod.NonStaticFactoryMethod"></bean>
	<bean id="p3" factory-bean="factory" factory-method="createPerson"></bean>
2.工厂代码以下:
/**
 * 使用非静态的工厂
 */
public class NonStaticFactoryMethod {
	/**
	 * 工厂的建立方法
	 * [@return](https://my.oschina.net/u/556800)
	 */
	public Person createPerson(){
	Person p = new Person();
	p.setUserName("lisi");
	p.setUserAge(20);
	return p;
}
}

测试方法以下:缓存

//非静态的方式
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		Person person = (Person) ctx.getBean("p3");
		System.out.println(person.toString());

静态方法: xml配置以下: <bean id="p4" class="com.flexible.factorymethod.StaticMethod" factory-method="createPerson"></bean> 工厂类以下: public class StaticMethod {session

/**
	 * 工厂的建立方法
	 *
	 * [@return](https://my.oschina.net/u/556800)
	 */
	public static Person createPerson() {
		Person p = new Person();
		p.setUserName("lisi");
		p.setUserAge(20);
		return p;
	}
}

测试方法: //静态的方式 Person person2 = (Person) ctx.getBean("p3"); System.out.println(person2.toString());ide

注入参数

Spring不但能够将String,int等字面值注入Bean中,还能够将集合,Map等类型的数据注入Bean中,还能够注入配置文件的其余Bean。函数

字面值

字面值标识可使用字符串表示的值,这些值能够经过<value></value>元素注入。xml中包含5个特殊字符,分别是&,<,>,`,'。若是包含这些这些特殊字符,就须要使用<![CDATA[]]特殊标签。测试

注意: 通常状况下,XML解析器会忽略元素标签内部字符串的先后空格,可是Spring却不会忽略元素标签内部字符串的字符串的先后空格,若是经过如下配置为属性注入值<property name="xx"><value> xxx xx</value></property>,那么先后,中间的空格将会被一块儿赋值给xx属性。flex

引入其余的Bean

xml文件以下:

<bean id="p5" class="com.flexible.beans.Person">
		<property name="userName" value="wangwu"></property>
		<property name="userAge" value="20"></property>
	</bean>
	<bean id="h1" class="com.flexible.beans.House">
		<property name="length" value="100"></property>
		<property name="width" value="80"></property>
		<property name="height" value="60"></property>
		<property name="person"><ref bean="p5"></ref></property>
	</bean>

<ref></ref>元素能够经过如下3个属性引用容器中的其余Bean。 1.bean:经过该属性能够引用同一容器或者父容器中的Bean,这是最多见的形式 2.local:经过该属性只能引用同一配置文件中定义的Bean,他能够利用XML解析器自动检验引用的合法性,以便开发者再编写配置时可以几时发现并纠正配置的错误。 3.parent:引用父容器中的Bean,如<ref parent="car">的配置说明car的Bean是父容器中的Bean.

Bean类 public class House {

private Double length;

	private Double width;

	private Double height;

	private Person person;
	public House() {
	}

	public Double getLength() {
		return length;
	}

	public void setLength(Double length) {
		this.length = length;
	}

	public Double getWidth() {
		return width;
	}

	public void setWidth(Double width) {
		this.width = width;
	}

	public Double getHeight() {
		return height;
	}

	public void setHeight(Double height) {
		this.height = height;
	}

	public Person getPerson() {
		return person;
	}

	public void setPerson(Person person) {
		this.person = person;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public String toString() {
		return "House{" +
				"length=" + length +
				", width=" + width +
				", height=" + height +
				", person=" + person +
				'}';
	}
}

测试例子:

public class BeanInjectDemo {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		House house = (House) ctx.getBean("h1");
		System.out.println(house.toString());
	}
}

内部Bean

内部Bean和Java的匿名内部类类似,既没有名字,也不能被其余的Bean引用,只能在声明处为外部Bean提供实例注入。 内部Bean几时提供了id,name,scope属性,也会被忽略。scope默认prototype类型。

<bean id="h1" class="com.flexible.beans.House">
		<property name="length" value="100"></property>
		<property name="width" value="80"></property>
		<property name="height" value="60"></property>
		<property name="person">
		<bean id="p5" class="com.flexible.beans.Person">
		<property name="userName" value="wangwu"></property>
		<property name="userAge" value="20"></property>
		</bean>
	</property>
	</bean>

null值

若是但愿给Bean的某个属性赋值一个null,若是使用<property name="xx"><value></value></property>这样是达不到预期的效果的,可是可使用<property name="xx"><null/></property>

级联属性

javaBean代码

public class Car {

	Person person = new Person();

	public Car() {
	}

	public Person getPerson() {
		return person;
	}

	public void setPerson(Person person) {
		this.person = person;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public String toString() {
		return "Car{" +
				"person=" + person +
				'}';
	}
}

xml配置以下:

<bean id="car" class="com.flexible.beans.Car">
		<property name="person.userName" value="zhaoliu"></property>
		<property name="person.userAge" value="20"></property>
	</bean>

测试代码:

public class CascadeDemo {

	public static void main(String[] args) {

		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		Car car = (Car) ctx.getBean("car");
		System.out.println(car.toString());
	}
}

集合属性注入

Favorite.java

public class Favorite {
	/**
	 * List集合
	 */
	private List<String> favoties = new ArrayList<>();
	/**
	 * map集合
	 */
	private Map<String,String> map = new HashMap<>();

	/**
	 *property,看做是特殊的map
	 */
	private Properties properties = new Properties();
	public Favorite() {
	}

	public List<String> getFavoties() {
		return favoties;
	}

	public void setFavoties(List<String> favoties) {
		this.favoties = favoties;
	}

	public Map<String, String> getMap() {
		return map;
	}

	public void setMap(Map<String, String> map) {
		this.map = map;
	}

	public Properties getProperties() {
		return properties;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	@Override
	public String toString() {
		return "Favorite{" +
				"favoties=" + favoties +
				", map=" + map +
				", properties=" + properties +
				'}';
	}
}

xml配置以下:

<bean id="favorities" class="com.flexible.beans.Favorite">
		<property name="favoties">
			<list>
				<value>游泳</value>
				<value>足球</value>
				<value>篮球</value>
				<value>棒球</value>
			</list>
		</property>
		<property name="map">
			<map>
				<entry>
					<key><value>k1</value></key>
					<value>运动</value>
				</entry>
				<entry>
					<key><value>k2</value></key>
					<value>上班</value>
				</entry>
				<entry>
					<key><value>k3</value></key>
					<value>约会</value>
				</entry>
			</map>
		</property>
		<property name="properties">
			<props>
				<prop key="k1">value1</prop>
				<prop key="k2">value2</prop>
				<prop key="k3">value3</prop>
			</props>
		</property>
	</bean>
	<!--集合的合并-->
	<bean id="childFavorities" parent="favorities">
		<property name="favoties">
			<list merge="true">
				<value>赛车</value>
			</list>
		</property>
		<property name="map">
			<map merge="true">
				<entry>
					<key><value>k12</value></key>
					<value>运动2</value>
				</entry>
				<entry>
					<key><value>k22</value></key>
					<value>上班2</value>
				</entry>
				<entry>
					<key><value>k32</value></key>
					<value>约会2</value>
				</entry>
			</map>
		</property>
		<property name="properties">
			<props merge="true">
				<prop key="k12">value12</prop>
				<prop key="k22">value22</prop>
				<prop key="k32">value32</prop>
			</props>
		</property>
	</bean>

测试代码:

public class CollectionAttributeInject {
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		Favorite favorite = (Favorite) ctx.getBean("favorities");
		System.out.println(favorite.toString());
		Favorite childFavorities = (Favorite) ctx.getBean("childFavorities");
		System.out.println(childFavorities.toString());
	}
}

方法替换

Spring支持使用一个Bean的方法取替换另外一个Bean的方法,替换别的Bean的Bean须要实现org.springframework.beans.factory.support.MethodReplacer接口,Spring利用该接口去替换目标Bean的方法.

被替换的类的方法:

public class Boss {
public House getHouse(){
House house = new House();
house.setHeight(1.0);
house.setLength(2.0);
house.setLength(3.0);
house.setWidth(4.0);
return house;
}
}

替换的类须要实现org.springframework.beans.factory.support.MethodReplacer接口。

public class Boss2 implements MethodReplacer{
	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		House house = new House();
		house.setHeight(1.01);
		house.setLength(2.01);
		house.setLength(3.01);
		house.setWidth(4.01);
		return house;
	}
}

xml配置文件以下:

<bean id="boss1" class="com.flexible.inject.methodreplace.Boss">
		<replaced-method name="getHouse" replacer="boss2"></replaced-method>
	</bean>
	<bean id="boss2" class="com.flexible.inject.methodreplace.Boss2"></bean>

<bean></bean>之间的关系

继承

父<bean></bean>主要功能是简化子<bean></bean>的配置,因此通常声明未abstract="true",表示这个bean不实例化为一个对应的Bean。

例子:

xml配置以下:

<!--这里将这个bean声明为abstract,代表它不会被实例化为bean-->
	<bean id="parent" class="com.flexible.beans.Parent" abstract="true">
		<property name="height" value="1.70"></property>
		<property name="weight" value="130"></property>
	</bean>
	<bean id="child" parent="parent">
		<property name="height" value="1.80"></property>
	</bean>

Parent.java

public class Parent {

	private String height;

	private String weight;

	public Parent() {
	}

	public String getHeight() {
		return height;
	}

	public void setHeight(String height) {
		this.height = height;
	}

	public String getWeight() {
		return weight;
	}

	public void setWeight(String weight) {
		this.weight = weight;
	}

	@Override
	public String toString() {
		return "Parent{" +
				"height='" + height + '\'' +
				", weight='" + weight + '\'' +
				'}';
	}
}

测试代码:

public class RelationBean {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		Parent parent = (Parent) ctx.getBean("child");
		System.out.println(parent.toString());
	}
}

依赖

在配置中若是当前的Bean的初始化须要前置条件(即这些前置条件都得初始化彻底了才能实例化这个Bean)知足的时候,这个时候就须要使用到依赖 depends-on=""

引用

若是须要引用到另外一个bean的id,须要使用<idref bean="xx"></idref>的方式使用,若是在同一个配置文件可使用<idref local="xx"></idref> 这样在开发的时期就能够辨别出错误。

整合多哥配置文件。

导入其余配置文件能够是import,例如:

<import resource="classpath:iocdemo2.xml"></import>

Bean做用域

低版本的Spring值支持两种做用域,因此才有singleton="true|false"的配置方式。Spring为了向后兼容,依然支持这种配置方式。推荐使用的配置方式:scope="<做用域类型>"

除了以上5种Bean做用域以外,Spring支持自定义Bean的做用域。经过org.springframework.beans.factory.config.Scope接口定义做用域,再经过org.springframework.beans.factory.config.CustomScopeConfigurer这个BeanFactoryPostprocessor注册自定义的Bean做用域。

singleton

Spring将Bean默认的做用域都是singleton,而且将实例化的Bean缓存再ApplicationContext的容器中,若是不但愿实例化以开始就进行,可使用lazy-init="true"实现懒加载,可是某些特殊的状况依然会提早加载。

prototype

scope="prototype"非单例做用域。它会将bean交给调用者,由调用者来管理bean的生命周期。

与Web应用环境相关的Bean做用域

<!--从类路径下加载Spring配置文件,classpath关键字特指类路径下加载-->
  <!--这里能够多个配置文件,建议采用逗号隔开的方式-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:flexible-context.xml</param-value>
	</context-param>
	<!--负责启动Spring容器的监听器,他将引用上面的上下文件参数获取Spring配置文件的地址-->
   <!--该监听器在web容器启动时自动运行,它会根据contextConfigLocation
   Web容器参数获取Spring配置文件,而且启动Spring容器。-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

正常的状况咱们只须要咱们只须要经过ContextLoadListoner(或者ContextLoadServlet)将Web容器与Spring容器进行整合,可是若是须要使用request,session,globalSession这三个WEB相关的做用域,就须要也配置requestContextListiner(实现了ServletRequestListener监听器接口),ContextLoadListener只是负责监听容器的启动和关闭。若是Spring须要request,session,globalSession的支持就须要获取Web容器的HTTP请求事件,此时RequestContestListener就能够实现这个需求。

配置以下:

<!--须要三大web容器做用域时须要这个配置-->
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>

request:请求结束后,实例就会销毁。 session:横跨整个HTTPSession ,Session的全部HTTP请求共享同一个实例,HTTPSession结束后,实例销毁。 globalSession做用域:globalSession做用域累死与session做用域,只能再Portlet的Web应用种使用。Portlet规范定义了全局Session的概念,它被组成的Portlet Web应用全部子Portlet共享,若是不在Portlet Web应用的环境下,那么globalSession==session做用域。

做用域依赖问题

须要启用<aop:scoped-proxy></aop:scoped-proxy>

FactoryBean

通常状况下,Spring经过反射机制利用<bean></bean>的class属性指定实现类实例化Bean。在某些状况下,实例化Bean的过程比较复杂,若是按照传统的方式,须要提供大量的配置信息。配置方式收到了限制。Spring为此提供了一个org.springframework.beans.factory.FactoryBean工厂类结论,用户可能够经过实现改工厂类接口定制实例化Bean的逻辑。 例子: HouseFactoryBean.java

public class HouseFactoryBean implements FactoryBean<House> {
   Person person;

	public Person getPerson() {
		return person;
	}
	//接受参数
	public void setPerson(Person person) {
		this.person = person;
	}
	//实例化bean
	@Override
	public House getObject() throws Exception {
	   House house = new House();
	   house.setLength(1.0);
	   house.setWidth(2.0);
	   house.setHeight(3.0);
	   house.setPerson(person);
		return house;
	}
	//返回Hourse类型
	@Override
	public Class<House> getObjectType() {
		return House.class;
	}
	//比闹事通FactoryBean返回的Bean是singleton
	@Override
	public boolean isSingleton() {
		return false;
	}

	@Override
	public String toString() {
		return "HouseFactoryBean{" +
				"person=" + person +
				'}';
	}
}

xml配置文件

<bean id="hoursebean" class="com.flexible.beans.HouseFactoryBean">
    <property name="person">
        <bean id="personp" class="com.flexible.beans.Person">
            <property name="userAge" value="20"></property>
            <property name="userName" value="zhouqi"></property>
        </bean>
    </property>
</bean>

测试代码:

public static void main(String[] args) throws Exception {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
    House house = (House) ctx.getBean("hoursebean");
    System.out.println(house.toString());
    //若是想拿到HouseFactoryBean实例,就须要 &hoursebean
    HouseFactoryBean houseFactoryBean = (HouseFactoryBean) ctx.getBean("&hoursebean");
    System.out.println(houseFactoryBean.toString());
    House object = houseFactoryBean.getObject();
    System.out.println(object.toString());
}

执行结果:

House{length=1.0, width=2.0, height=3.0, person=Person{userName='zhouqi', userAge=20}}
HouseFactoryBean{person=Person{userName='zhouqi', userAge=20}}
House{length=1.0, width=2.0, height=3.0, person=Person{userName='zhouqi', userAge=20}}

基于注解的配置

使用注解定义Bean

Spring提供了四个定义Bean的注解,分别是:

1.@Repository:用于对DAO实现类进行标注

2.@Service:用于对Service实现类进行标注。

3.@Controller:用于对Controller实现类进行标注

4.@Component能够代替上面任何一个,只不过上面这些标注有特殊的用途,能一目了然的看出来这个bean的用途。

扫描注解定义的Bean

Spring提供了一个Context命名空间,它提供了经过扫面类包以应用注解定义Bean的方式。 例如:<context:component-scan base-package="com.flexible"></context:component-scan>

若是但愿扫描的是特定的类而非基础包下的类,可使用resource-pattern熟悉过滤

<context:component-scan base-package="com.flexible" resource-pattern="xx/*.class"></context:component-scan>

若是还须要更加具体的过滤,如实现了xx接口的,

例如:

<context:component-scan base-package="com.flexible">
		<context:include-filter type="aspectj" expression="com.flexible..*Controller+"></context:include-filter>
	</context:component-scan>

自动装配

使用 @Autoired

@Qualifier知道注入的Bean的名称。

对标准注解的@Resource(须要提供一个Bean的名字)和@Inject的支持。

Bean做用范围及生命过程的方法。

@Scope("prototype")

Bean做用范围以及生命过程方法

在使用<bean></bean>进行配置时,能够经过init-method和destroy-method属性指定Bean的初始化以及容器销毁前执行的方法。Spring从2.5开始支持JSR-250中定义的@PostConstruct和@PreDestroy注解,在Spring中他们至关于init-method和destroy-method属性的功能,不过在使用注解时,能够在一个Bean中定义多个@PostConstruct和@PreDestroy方法。

例子:

LifeCyleBea.java

@Component
public class LifeCyleBean {
	private String info;
	@PostConstruct
	public void initMethod(){
		System.out.println(".....this is initMethod....");
	}
	@PreDestroy
	public void destroyMethod(){
		System.out.println(".....this is destroyMethod....");
	}

	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}
}

xml配置文件开启包扫描:

<context:component-scan base-package="com.flexible">
	</context:component-scan>

测试代码:

public class LifeCycleDemo {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:iocdemo.xml");
		((ClassPathXmlApplicationContext)context).destroy();

	}
}

执行结果: .....this is initMethod....

.....this is destroyMethod....

基于Java类的配置

使用Java类提供的Bean定义信息

JavaConfig是Spring的一个子项目,旨在经过Java类的方式提供Bean的定义信息,在Spring2.0开始发布。普通的POJO只有标注@Configuration注解,就能够为Spring容器提供Bean定义的信息。每一个标注了@Bean得类方法都至关于提供了一个Bean的定义信息。经过代码进行配置要灵活点,可是配置文件配置要简洁些。

AppConfig.java

//将一个POJO标注为定义Bean的配置类
@Configuration
public class AppConfig {
//能够给bean一个名字,还能够配置做用域
//@Bean(name = "car")
@Bean
public Car getCar(){
	Car car = new Car();
	car.setPerson(getPerson());
	return car;
}
@Bean
public Person getPerson(){
	 Person person = new Person();
	 person.setUserName("王八");
	 person.setUserAge(28);
	 return person;
}
}

测试代码:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    Car car = (Car) context.getBean(Car.class);
    System.out.println(car.toString());

使用基于Java类的配置信息启动Spring容器

1.直接经过@Configuration类启动Spring容器

Spring提供了一个AnnotationConfigApplicationContext类,它可以直接经过标注¥Configuration的Java启动Spring容器。 例如:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotationConfigApplicationContext还支持经过编码的方式加载多个@Configuration配置类,而后刷新容器应用的配置类。 例如:

ApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.register(AppConfig2.class);
context.refresh()

经过XML配置文件引用@Configuration的配置

标注了@Configuration的配置类与标注了@Component的类同样也是一个Bean,它能够被Spring的context:compent-scan</context:compent-scan>扫描到相应的配置类便可。 例如:

<context:component-scan base-package="com.flexible" resource-pattern="AppConfig.class">

经过@Configuration配置类引用XML配置信息

在@Configuration配置类中能够经过@ImportResource引入XML配置文件,在RefXmlConfig配置类中便可直接经过@Autowired引用XML配置文件定义的Bean. RefXmlConfig.java

@Configuration
@ImportResource("classpath:iocdemo2.xml")
public class RefXmlConfig {
@Bean
@Autowired
public PenBox getPenBox(PenBox penBox){
	return penBox;
}
}

PenBox.java

public class PenBox {

private String markName;

private String description;

	public PenBox() {
	}

	public String getMarkName() {
		return markName;
	}

	public void setMarkName(String markName) {
		this.markName = markName;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public String toString() {
		return "PenBox{" +
				"markName='" + markName + '\'' +
				", description='" + description + '\'' +
				'}';
	}
}

测试代码:

public class RefXmlDemo {
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(RefXmlConfig.class);
		PenBox penBox = (PenBox) context.getBean("penBox");
		System.out.println(penBox.toString());
	}
}

执行结果:

PenBox{markName='xxx', description='description'}
相关文章
相关标签/搜索