Spring基础知识

依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。
~~依赖倒置原则,它转换了依赖,高层模块不依赖于低层模块的实现,而低层模块依赖于高层模块定义的接口。通俗的讲,就是高层模块定义接口,低层模块负责实现。
控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。
~~控制反转,创建被调用者的工作不再由调用者来完成,即依赖对象不在被依赖模块的类中直接通过new来获取。
依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。
~~它一种重要的方式,创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者。
IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。
~~IoC容器是DI构造函注入的框架,它管理着依赖项的生命周期以及映射关系。
AOP(Aspect Oriented Programming) 面向切面编程

~~定义一个切面,在切面的纵向定义处理方法,处理完成之后,回到横向业务流。


依赖注入是指程序运行过程中,如果需要另一个对象写作时,无须在代码中创建被调用者,而是依赖于外部容器的注入。
依赖注入通常有两种:
1、设值注入:IOC容器使用属性的Setter方法来注入被依赖的实例。
2、构造注入:IOC容器使用构造器来注入被依赖的实例。

由于大量的构造器参数可能使程序变得笨拙,特别是当某些属性是可选的时候。因此通常情况下,Spring开发团队提倡使用setter注入。

主要使用构造器注入的方式配置bean时,很有可能会产生循环依赖的情况。对于此问题,一个可能的解决方法就是修改源代码,将构造器注入改为setter注入。另一个解决方法就是完全放弃使用构造器注入,只使用setter注入。  

Spring配置Beans实例通常指定2个属性:
1、id:该Bean的唯一标识
2、class :该Bean的实现类,不能是接口 。Spring容器会用xml解析器读取该属性,并用反射创建该类实例。

Spring会为每一个bean注入属性,所以bean元素的class属性只能是实现类。

设值注入的时候,Spring会自动接管每个bean里的property。Spring会在调用无参数构造器、创建默认的bean实例后,调用setter方法为程序注入属性值。

构造注入的时候,配置文件要在bean中使用<construcor-arg />元素指定构造器参数。


Spring容器最直接的接口就是BeanFactory

实例化Spring容器BeanFactory

Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

或者:

ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

或者:

ApplicationContext context = new ClassPathXmlApplicationContext(
        new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) context;

ApplicationContext接口

它由BeanFactory接口派生而来,因而提供了BeanFactory所有的功能。为了以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,context包还提供了以下的功能:

  • MessageSource, 提供国际化的消息访问

  • 资源访问,如URL和文件

  • 事件传播,实现了ApplicationListener接口的bean

  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

由于ApplicationContext包括了BeanFactory所有的功能,所以通常建议优先采用ApplicationContext


Bean可以获取Spring容器
实现BeanFactoryAware的Bean可以访问创建这个Bean的BeanFactory。
实现ApplicationContextAware的Bean可以访问创建这个Bean的Spring容器。

但是实现ApplicationContextAware接口让Bean拥有了访问容器的能力,污染了代码,导致代码与Spring接口耦合在一起。所以不是特别必要,建议不要直接访问容器。


Spring中的Bean
对于开发者来说使用Spring框架所做的就是两件事:1、开发Bean;2、配置Bean
对于Spring框架来说,就是根据配置文件创建Bean实例,并调用Bean实例的方法完成依赖注入,这就是IOC的本质。

配置文件中通过<bean id="xxx" class="lee.xxxClass">方法配置一个Bean时,这样要求该Bean实现类必须有一个无参数的构造器,底层相当于调用了xxx=new lee.xxxclasss()

Bean作用域有singleton单例模式、prototype原型模式、request、session、global session,不指定scope时默认使用singleton。对于Singleton作用域的Bean,如果没有强行取消其预初始化行为,系统会在创建Spring容器的时候预初始化所有Singleton Bean,该Bean所依赖的Bean也被一起初始化。


BeanFactory在需要Bean的时候创建Bean,ApplicationContext在创建ApplicationContext实例的时候就会初始化所有的Bean。所以后者实例化过程会比前者实例化过程的时间和内存开销大,但可以再容器初始化阶段就检验处配置错误。

lazy-init="true"会强制取消Bean实例预初始化。

Spring的作用是管理JavaEE组件,Spring把所有的Java对象都成为Bean,所以可以把任何Java类都部署在Spring容器中,只有该Java类有相应的构造器即可。例如下面的片段:

<bean id="id" class="lee.AClass">
    <property name="aaa" value="aVal"></property>
    <property name="bbb" value="bVal"></property>
</bean>

 这个时候Spring采用类似于这样的方法创建Java实例: 
 

Class targetClass=Class.forName("lee.AClass");
Object bean=targetClass.newInstance();
然后又这样注入属性:

String setName1="set"+"Aaa";
Method setMethod1=targetClass.getMethod(setName1,aVal.getClass());
setMethod1.invoke(bean,aVal);


可以用autowire属性自动装配注入bean元素,autowire有四种属性:byName,byType,constructor,autodetect.

byName:根据属性名自动装配

byType:根据属性类型自动装配

constructor:与byType相似,但是是用于构造器注入的参数

autodetect:根据Bean内部结构决定是constructor还是byType,如果找到一个默认的构造函数,就会使用byType。

如果不想Spring判断某些bean,可以设置autowire-candidate="false"。


给bean注入嵌套bean和使用ref引用容器中另一个bean本质是一样的。

给bean注入集合的时候可以使用<list><set><map><props>分别设置List,Set,Map,Properties.

给bean的属性指定值的时候可以采用组和属性名的方式,但是除了最后一个属性外其他属性都不能为null,因为spring时先调用get方法,然后调用set方法。

Spring每个property属性对应调用一次setter方法,当传入一个java对象作为形参的时候有两种方式:1、使用ref引用一个容器中已配置的bean对象;2、使用bean配置一个嵌套Bean。

创建Bean通常有如下方法:
1、通过构造器创建Bean实例
2、通过静态工厂方法创建Bean实例
3、通过实例工厂方法创建Bean实例


构造器方法创建Bean实例执行过程:
~~程序创建ApplicationContext实例
~~通过类的构造器创建默认实例
~~根据配置文件注入依赖关系:先实例化依赖bean,然后将依赖bean注入(调用set方法)
~~返回所要创建Bean的实例

静态工厂方法创建<bean>实例时需要制定2个属性
class:静态工厂类的类名
factory-method:生产bean实例的静态工厂方法
如果静态工厂方法需要参数,就是用< constructor-arg/>传入。
<property>配置普通依赖,注入属性

实例工厂方法创建bean必须将实例工厂配置成Bean实例。
调用实例工厂方法创建Bean必须使用factory-bean属性确定工厂。

追踪getBean方法

Alt+getBean显示的是上层接口BeanFactory中的getBean方法。
于是查看ClassPathXmlApplicationContext。
ClassPathXmlApplicationContext是很多个父类的子类,其中AbstractApplicationContext类实现了getBean方法,getBean又调用了getBeanFactory返回了ConfigurableListableBeanFactory
public Object getBean(String name, Class requiredType)
        throws BeansException
    {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(name, requiredType);
    }
    
public abstract ConfigurableListableBeanFactory getBeanFactory()
    throws IllegalStateException;
getBeanFactory()方法是一个抽象方法,所以必定在AbstractApplicationContext的子类中实现getBeanFactory方法,然后看其子类,子类AbstractRefreshableApplicationContext中有getBeanFactory方法。
public final ConfigurableListableBeanFactory getBeanFactory()
{
    Object obj = beanFactoryMonitor;
    JVM INSTR monitorenter ;
    if(beanFactory == null)
        throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
    return beanFactory;
    Exception exception;
    exception;
    throw exception;
}
private DefaultListableBeanFactory beanFactory;
返回的beanFactory是DefaultListableBeanFactory的实例,然后到这个实例中去看,就可以找到public transient Object getBean(Class requiredType, Object args[])
            throws BeansException 真正的源代码了。

抽象bean用于继承,可以不指定class。ApplicationContext预初始化会忽略所有的抽象Bean定义。

<bean id="steelAxe" class="org.crazyit.app5.service.impl.SteelAxe"/>
<bean id="stoneAxe" class="org.crazyit.app5.service.impl.StoneAxe"/>
<!-- 通过abstract属性定义该Bean 是抽象Bean -->
<bean id="chineseTemplate" class="org.crazyit.app5.service.impl.Chinese"
    abstract="true">
    <property name="axe" ref="steelAxe"/>
</bean>
<!-- 通过parent属性定义子bean -->
<bean id="chinese" parent="chineseTemplate">
    <!-- 覆盖父Bean中依赖关系的配置 -->
    <property name="axe" ref="stoneAxe"/>
</bean>
    Spring实例化依赖bean:SteelAxe实例...
    Spring实例化依赖bean:StoneAxe实例...
    Spring实例化主调bean:Chinese实例...

    Spring执行依赖关系注入...

抽象bean,指定abstract="true",它的价值在于被继承,Spring容器会忽略所有的抽象Bean定义,与初始化时不初始化抽象bean。

当有一批Bean配置的大量配置信息完全相同,只有少量配置不同,那么我们可以先为这批bean配置一个bean模板,将这批bean中相同的信息配置成bean模板,因为spring容器无须配置bean模板的实例,所以通常将这个bean模板配成抽象bean。

Spring中的继承,子Bean和父Bean可以是不同类型,是实例间的关系,表现为参数值的延续。

java中的继承,子类是一种特殊的父类,是类之间的关系,表现为方法,属性的延续。

//配置文件
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<!-- 通过abstract属性定义该Bean 是抽象Bean -->
	<bean id="chineseTemplate" class="org.crazyit.app7_8.service.impl.Chinese"
		abstract="true">
		<property name="axe" ref="steelAxe"/>
	</bean>
	<!-- 通过parent属性定义子bean -->
	<bean id="chinese" parent="chineseTemplate">
		<!-- 覆盖父Bean中依赖关系的配置 -->
		<!-- <property name="axe" ref="stoneAxe"/> -->
	</bean>
	<!-- 定义两个Axe实例 -->
	<bean id="steelAxe" class="org.crazyit.app7_8.service.impl.SteelAxe"/>
	<bean id="stoneAxe" class="org.crazyit.app7_8.service.impl.StoneAxe"/>
</beans>

//测试代码
public class BeanTest7_8 {
    public static void main(String[] args)
    {
        ApplicationContext ctx = new
            ClassPathXmlApplicationContext("com/ccnu/xml/bean7_8.xml");
        Person p1 = ctx.getBean("chinese" , Person.class);
        Person p2 = ctx.getBean("chinese" , Person.class);
        p1.useAxe();
        System.out.println(p1 == p2);
    }
}

//结果
Spring实例化主调bean:Chinese实例
Spring实例化依赖bean:SteelAxe实例
Spring执行依赖关系注入
Spring实例化依赖bean:StoneAxe实例
钢斧砍柴真快
true
在上面代码的执行顺序可以看出,spring容器先按照bean.xml中的singleton顺序开始实例化,ChineseTemplate是抽象类,不实例化,所以先实例化Chinese,然后实例化依赖对象SteelAxe,然后注入依赖对象,最后实例化StoneAxe。


可以通过实现FactoryBean接口来创建容器工厂,当用getBean()方法来获取工厂Bean时,容器不会返回FactoryBean实例,而是返回FactoryBean的产品,如果要返回FactoryBean实例的时候则在Bean id上加上"&"符号。


当要让指定Bean在目标Bean之前初始化的时候,可以使用depend-on属性。

对于Singleton作用域的bean,Spring容器可以跟踪Bean实例的产生,销毁。但是对于prototype作用域的Bean,Spring无法知道曾经创建了多少个prototype作用域的Bean,也不知道什么时候被销毁。

Spring提供两种方式在Bean全部属性设置成功后执行特定行为:

1、使用init-method属性
2、实现InitializingBean接口(实现afterPropertiesSet()方法)
Sping在完成依赖注入之后,会先调用afterPropertiesSet()方法,然后调用init-method属性对应的方法。

Spring提供两种方式在Bean实例销毁之前执行特定行为:
1、使用destroy-method属性

2、实现Disposable接口(实现destroy()方法)

Sping在容器关闭之前,会先调用destroy()方法,然后调用destroy-method属性对应的方法。

在非web应用的环境下,要使Spring容器关闭,可以调用AbstractApplicationContext中的registerShutdownHook()方法。

当Bean实现了ApplicationContextAware接口,BeanNameAware接口之后,Spring容器会在该Bean初始化完成之后,也就是调用init-method属性所指定的方法之后,再回调setApplicationContext和setBeanName方法。


协调作用域不同步的bean:

当Singleton作用域bean依赖prototype作用域bean时,Spring容器初始化时会初始化所有的Singleton bean,此时prototype bean被创建,并注入到Singleton bean,当Singleton bean被创建完后,就持有了一个prototype bean,之后就不会再为Singleton bean执行注入了。所以当客户端多次请求Singleton bean,并调用prototype bean的方法是,始终是调用同一个prototype bean实例,就违背了设置prototype bean的初衷:本来希望它具有prototype行为,但是表现出Singleton行为了。

这个时候一般我们采用方法注入:

方法注入通常使用lookup方法注入,利用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的bean通常是一个non-singleton bean。

例如:

public abstract class Chinese implements Person{
	public Chinese()
	{
		System.out.println("Spring实例化主调bean:Chinese实例...");
	}
	
	//定义一个抽象方法,该方法将由Spring负责实现
	public abstract Axe getAxe();
	public void useAxe()
	{
		System.out.println("正在使用 " + getAxe()
			+ "砍柴!");
		System.out.println(getAxe().chop());
	}
}

<bean id="steelAxe" class="org.crazyit.app7_9.service.impl.SteelAxe" 
    scope="prototype"/>
<bean id="chinese" class="org.crazyit.app7_9.service.impl.Chinese">
    <!-- 指定getAxe方法返回steelAxe
        每次调用getAxe方法将获取新的steelAxe对象 -->
    <lookup-method name="getAxe" bean="steelAxe"/>
</bean>
所以使用lookup方法注入后,系统每次调用getAxe()方法都将生成一个新的SteelAxe实例,这样可以保证当Singleton作用于的Bean需要全新的Bean实例时,直接调用getAxe()方法即可,从而避免一直使用最早注入的bean实例。

在Spring配置文件中使用xml元素进行配置,实际上是让spring执行相应的代码:

1、使用<bean.../>元素,实际上是让Spring执行无参数构造器,或有参数的构造器。

2、使用<property.../>元素,实际上是让Spring执行一次setter方法。

除此之外,spring还对java程序的其他方法提供了对应的配置语法:

1、调用getter方法:使用PropertyPathFactoryBean

2、访问类或对象的Field值:使用FieldRetrievingFactoryBean

3、调用普通方法:使用MethodInvokingFactoryBean


PropertyPathFactoryBean用来获得目标Bean的属性值(实际上就是它的getter方法的返回值),获得的值可注入其他bean,也可直接定义成新的bean。

将bean实例的属性值注给另一个bean:

<bean id="person" class="org.crazyit.app7_10.service.Person">
    <!-- 为age属性指定值 -->
    <property name="age" value="30"/>
    <property name="son">
        <!-- 使用嵌套Bean定义属性值 -->
        <bean class="org.crazyit.app7_10.service.Son">
            <property name="age" value="11" />
        </bean>
    </property>
</bean>

<bean id="son2" class="org.crazyit.app7_10.service.Son">
    <property name="age">
        <!-- 以下是访问Bean属性的简单方式,这样可以将person Bean的son属性的、
            age属性赋值给son2这个bean的age属性-->
        <bean id="person.son.age" class=
            "org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
    </property>
</bean>

将bean实例的属性值直接定义成bean实例:

此时要指定2个属性值:targetBeanName或targetObject、propertyPath

<bean id="son1"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
    <!-- 确定目标Bean,表明son1 Bean来自哪个Bean的属性 -->
    <property name="targetBeanName" value="person"/>
    <!-- 确定属性表达式,表明son1 Bean来自目标bean的哪个属性 -->
    <property name="propertyPath" value="son"/>
</bean>

FieldRetrievingFactoryBean可以获得其他bean的field值,然后注入给其他bean,也可直接定义成新的bean。

将bean的field值直接指定成bean实例,需要指定2个属性:targetClass或targetObject、targetField


MethodInvokingFactoryBean可以将获得的方法返回值注入到指定bean实例的指定属性,也可以直接定义成bean实例。

实例方法指定targetObject、targetMethod。

静态方法指定targetClass、targetMethod。

如果要确定调用目标方法的参数,就如下这样指定:

<bean id="javaVersion" class=
    "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <!-- targetObject确定目标Bean,确定调用哪个Bean -->
    <property name="targetObject" ref="sysProps"/>
    <!-- targetMethod确定目标方法,确定调用目标Bean的哪个方法 -->
    <property name="targetMethod" value="getProperty"/>
    <!-- 确定调用目标方法的参数 -->
    <property name="arguments">
        <!-- list元素列出调用方法多个参数值 -->
        <list>
            <value>java.version</value>
        </list>
    </property>
</bean>

从Spring2.0开始,Spring允许使用基于XML Schema的配置方式来简化Spring配置文件。使用p名称空间配置属性和util Schema可大大简化Spring的配置。

bean后处理器

bean后处理器负责对容器中其他的bean执行后处理,它必须实现BeanPostProcessor接口,实现postProcessBeforeInitialization、postProcessAfterInitialization方法,这两个方法分别在目标bean初始化之前、初始化之后被回调,用于对容器中的bean实例进行增强处理。

bean后处理器两个方法的回调时机:

Spring基础知识_第1张图片

容器后处理器

Spring还提供了容器后处理器,此处理器必须实现BeanFactoryPostProcessor接口,实现postProcessBeanFactory(configurablelistablebeanfactory)方法,只对容器本身进行处理。





你可能感兴趣的:(java,spring,AOP,框架)