精通Spring+4.x++企业开发与实践之IOC容器

#IOC容器

#类装载器ClassLoader

寻找类的字节码文件并构造出类再JVM内部标识对象的组件。再Java中,
类装载器吧一个类装入JVM,需要入如步骤:
(1)装载:查找和导入Class文件.
(2)链接:执行校验,准备和解析步骤,其中解析步骤是可以选择的。
    1.准备:给类的静态遍历分配存储空间。
    2.校验: 检查载入Class文件数据的正确性。
    3.解析:将符好引用转换成直接引用。
(3)初始化:对类的静态变量,静态代码块执行初始化工作.

#JVM装载类时使用的机制

JVM装载类时使用"全盘负责委托机制","全盘负责"是指当一个ClassLoader装载一个类时
,除非显示的使用另一个ClassLoader,该类所依赖的类也由这个ClassLoader载入;"委托机制"
是指原先委托父装载器寻找目标类,只有找不到的情况下才从自己的类路径中查找并且装载目标类
,这也是出于安全考虑。

#Java的反射机制

Class反射对象描述类雨衣结构,可以从class对象重获取构造函数,成员变量,方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义。
	- Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获取类的所有构造函数反射对象数组,在Java5.0中,还可以通过getConstructor(Class...parameterTypes)获取特定入参的构造函数反射对象。Constructor的一个主要方法事newInstance(Object[] initargs),通过改方法可以创建一个对象的实例,相当于使用new关键字。在Java5.0中,改方法演化为更为灵活的形式:newInstance(Onject)
	- Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[].在Java5.0中,可以通过getDeclaredMethod(String name,Class...parameterTypes)获取特定签名的方法
	name是方法名,Class...为方法入参类型列表。Method最主要的方法是invoke(Object obj,Object[] args),其中obj标识操作的目标对象;args为方法入参。Method的其他方法如下
	getTypeParameters
	getReturnType
	getGenericReturnType
	getParameterTypes
	getExceptionTypes

	- Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的程愿变量反射对象数组,通过Class#getDeclaredField(String name)则可以获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj,Object value),其中object标识操作的目标对象,通过value为目标对象的成员变量设置值,如果为基础类型可以使用Field类中提供的待类型名的值设置方法。

实例: github:https://github.com/chenanddom/SpringSection4/blob/master/src/main/java/com/flexible/ioc/reflect/ReflectDemo.java

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
		//类装载器获取Car类对象
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		Class clazz = loader.loadClass("com.flexible.ioc.reflect.Person");
		//获取类的默认构造器对象并且通过它实例化
//        Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
		Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
		Person person = (Person) constructor.newInstance();
		//通过反射方法设置属性
		Method setUserName = clazz.getMethod("setUserName", String.class);
		setUserName.invoke(person, "zhangsan");
		Method setUserAge = clazz.getMethod("setUserAge", Integer.class);
		setUserAge.invoke(person, 26);
		System.out.println(person.toString());
//        System.out.println(setUserAge.getReturnType());//获取返回值类型
//        System.out.println(setUserAge.getParameterTypes());//获取参数类型
		System.out.println(setUserAge.getParameterAnnotations());
		System.out.println(loader);
		System.out.println(loader.getParent());
		//根记载器不是ClassLoader的子类,是C++写的,无法获取到
		System.out.println(loader.getParent().getParent());
	}
}

#资源访问

JDK缺少从类路径或者Web容器上下文中获取资源的操作类。Spring针对此限制设计了一个Resource接口,它为应用提供了更强的底层资源访问能力。该接口用于对应的不同的资源类型的实现类。Resource接口的主要方法如下:
	//资源是否存在
	boolean exists();
	//资源是否可读
	boolean isReadable();
	//资源是否被打开
	boolean isOpen();
	//如果底层资源可以标识成URL,则该方法返回对应的URL对象
	URL getURL() throws IOException;
	//获取图片的uri
	URI getURI() throws IOException;
	//底层资源对应一个文件,则该方法返回对应的File对象
	File getFile() throws IOException;
	//
	long contentLength() throws IOException;

	long lastModified() throws IOException;

	Resource createRelative(String var1) throws IOException;

	String getFilename();

	String getDescription();
	//因为集成了InputStreamSource,所以也有获取流的方法
	InputStream getInputStream() throws IOException;

Resource在Spring起到了不可获取的作用,Spring框架使用Resource装载各种资源,包括配置文件资源,国际化属性文件资源等。它的具体实现类图如下:

- WritableResource:可写资源接口,Spring3.1新家的接口,实现累如图所示,FileSystemResource和PathResource(Spring4.0加的)
- ByteArrayResource->AbstractResource:继承了AbstractResource,标识二进制数组的资源,二进制数组的资源可以在内存中通过程序构造。
- ClassPathResource:类路径下的资源,资源以相对路径的方式标识,如果代码所示:

public class FileSourceDemo {

public static void main(String[] args) {

    String filePath = "E:\\BaiduNetdiskDownload\\wangpan\\SpringSection4\\src\\main\\resources\\conf\\file1.txt";
    try {
    //使用系统文件路径方式加载文件
    WritableResource res1 = new PathResource(filePath);
    //使用类路径方式加载文件
    Resource res2 = new ClassPathResource("conf/file1.txt");
    //使用WritableResource接口写资源文件
        OutputStream stream1 = res1.getOutputStream();
        stream1.write("这是使用spring Resource接口的例子".getBytes());
        stream1.close();
     //使用Resource接口读资源文件
        InputStream ins1 = res1.getInputStream();
        InputStream ins2 = res2.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int i;
        while ((i=ins1.read())!=-1){
            baos.write(i);
        }
        System.out.println(baos.toString());
        System.out.println("res1:"+res1.getFilename());
        System.out.println("res2:"+res2.getFilename());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

-FileSystemResource:文件系统资源,资源以文件系统路径的方式所示,如D:/conf/bean.xml.
- InpustreamResource:以输入流返回标识的资源。
- ServletContextResource:为访问Web容器上下文中的资源而设置的类,负责以相对于web应用根目录的路径加载资源,它支持以流和URL的方式访问,在WAR解包的情况下,也可以通过File方式访问。该类还可以直接从jar包中访问资源。
- UrlResource:URL封装了java.net.URL,它使用户能够访问任何可以通URL标识的资源,如文件系统的资源,HTTP资源,FTP资源等。
-PathResource:Spring4.0提供的读取资源文件的新类,Path封装了java.net.URL,java.nio.file.Path(Java7提供),文件系统资源,它使用户呢刚刚访问任何可以通过URL,Path,系统文件路径标识的资源,文件系统,HTTP,FTP资源。

##资源加载 为了访问不类型的资源,需要使用相应的Resource实现类,这是比较麻烦的。但是Spring提供了一套强大的加载资源的的机制,不但可以通过"classpath:","file:"等资源地址前缀识别不同的资源类型,还支持Ant风格带通配符的资源地址。

资源表达式如下图所示:

##资源加载器

ResourceLoader接口仅有一个getResource(String location)方阿飞,可以根据一个资源地址加载文件资源。不过,资源地址仅仅支持带自u按类型前缀得表达式,不支持Ant风格得资源路径表达式。ResourcePatternResolver拓展ResourceLoader接口,定义了一个新得接口方法getResources(String locationPattern),该方法支持带资源类型前缀及Ant风格得资源路径表达式。PathMathchingResourcePatternResolver是Spring提供得标准实现类。

代码实现:

package com.flexible.resources;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static junit.framework.TestCase.assertNotNull;

public class PatternResolverDemo {
	public static void main(String[] args) throws IOException {
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		//加载所有类包com.flexible.resources下的,以.xml为后缀的资源文件
		Resource[] resources = resourcePatternResolver.getResources("classpath:com/flexible/resources/**/*.xml");
		assertNotNull(resources);
		List<Resource> resourceList = Arrays.asList(resources);
		resourceList.forEach(e->{
			System.out.println(e.getFilename());
		});
	}
}

提示: 使用Resource操作文件时,如果资源配置文件在项目发布时会被达到JAR中,,那么不能使用Resource#getFile()方法,否则会被抛出FileNotFoundException.但是可以使用Resource#getInputStream方法读取。 错误的方式: (new DefaultResourceLoader()).getResource("classpath:conf/sys.properties").getFile() 正确的读取方式: (new DefaultResourceLoader()).getResource("classpath:conf/sys.properties").getInputStream() 在Jar包中getFile()无法读取,应该尽量使用流来处理。

#BeanFactory和ApplicationContext 在Spring中是通过配置文件描述Bean和Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并简历 Bean之间的依赖关系。除此之外还提供了Bean的实例缓存,生命周期管理,Bean实例代理,事件发布,资源装载等高级服务。 一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文,但是为了方便也将ApplicationContext成为Spring容器。BeanFactory是面向Spring本身的,ApplicationContext是面向开发者的。

###BeanFactory 可以被创建和管理的Java对象常被成为Bean,但是这个JavaBean是要满足一定的规范的,例如需要提供ige默认的不带参数的构造函数,不依赖于某特定的容器等,但是Spring的Bean比JavaBean更加的广泛一些,所以可以被Spring容器实例化并甘丽的Java类可以成为Bean。

  • BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean.BeanFactory的功能通过其他接口得到不断的拓展。

    • ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法,例如查看Bean的个数,获取某一类型的Bean配置名,查看容器是否包括某一个Bean等。 containsBeanDefinition getBeanDefinitionCount getBeanDefinitionNames getBeanNamesForType getBeanNamesForType getBeanNamesForType getBeansOfType getBeansOfType getBeanNamesForAnnotation getBeansWithAnnotation findAnnotationOnBean
    • HierachicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口方法访问父容器的接口。

      getParentBeanFactory
      containsLocalBean
      
    • CofigurableBeanFactory:这是一个重要的接口增强了IoC容器的可定制性。它定义了设置类装载器的,属性编辑器,容器初始化后置处理器等方法。 setParentBeanFactory setBeanClassLoader getBeanClassLoader setTempClassLoader getTempClassLoader setCacheBeanMetadata isCacheBeanMetadata setBeanExpressionResolver getBeanExpressionResolver setConversionService getConversionService addPropertyEditorRegistrar registerCustomEditor copyRegisteredEditorsTo setTypeConverter getTypeConverter addEmbeddedValueResolver hasEmbeddedValueResolver resolveEmbeddedValue addBeanPostProcessor getBeanPostProcessorCount registerScope getRegisteredScopeNames getRegisteredScope getAccessControlContext copyConfigurationFrom registerAlias resolveAliases getMergedBeanDefinition isFactoryBean setCurrentlyInCreation isCurrentlyInCreation registerDependentBean getDependentBeans getDependenciesForBean destroyBean destroyScopedBean destroySingletons
    • AutowireCapableBeanFactory:定义了将容器中的Bean按照某种规则(名字,类型匹配等)进行自动装配的方法。

      createBean
      autowireBean
      configureBean
      createBean
      autowire
      autowireBeanProperties
      applyBeanPropertyValues
      initializeBean
      applyBeanPostProcessorsBeforeInitialization
      applyBeanPostProcessorsAfterInitialization
      destroyBean
      resolveNamedBean
      resolveDependency
      resolveDependency
      
    • SingletonBanRegistry:定义允许在运行期间向容器注册单实例Bean的方法,

      registerSingleton
      getSingleton
      containsSingleton
      getSingletonNames
      getSingletonCount
      getSingletonMutex
      
    • BeanDefinitionRegistry:Spring配置文件中每一个<bean></bean>节点元素在Spring容器里都通过一个BeanDefinition对象标识,它描述了Bean的配置信息,而BeanDefinitionRegistry接口提了向容器手工注册BeanDefinition对象的方法。 registerBeanDefinition removeBeanDefinition getBeanDefinition containsBeanDefinition getBeanDefinitionNames getBeanDefinitionCount isBeanNameInUse

#WebApplicationContext类体结构 WebApplicationContext是专门为Web准备的,它允许从相对于web根目录的路径装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文。Spring专门为此提供了已个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,可以从ServletContext中获取WebApplicationContext实例。WebApplicationContext下的Bean除了singleton和prototype两种作用域之外还有request,session和global session

由于Web应用比一般的应用有更多的特性,因此WebApplicationContext拓展了ApplicationContext.WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文起到的时候,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,可以通过以下语句从Web容器中获取WebApplicationContext:

WebApplication wac = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

这个就是WebApplicationContextUtils工具类getWebApplicationContext(ServletContext sc)方法的内部实现方式,这样Spring的Web应用上下文就和Web容器上下文应用就可以实现互访。

ConfigurableWebApplicationContext拓展了WebApplication,它允许通过配置的方式实例化WebApplicationContext,同时定义了两个重要的方法

  • void setServletContext(ServletContext servletContext);为spring设置web应用上下文,以便二者结合

  • void setConfigLocations(String... configLocations);允许加载带前缀的配置文件

###父子容器

通过HierachicalBeanFactory接口,Spring的IoC容器可以简历父子层级关联的容器体系,子容器可以访问父容器的Bean,但是父容器无法访问自容器的内容,SpringMvc中,展现层Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中,这样展现层就可以引用业务层和持久层。而业务和持久层看不到展现层的Bean。

#Bean的生命周期

###BeanFactory中Bean的生命周期 具体的过程如下:

1.当调用者通过getBean(beanName)向容器请求某一个Bean时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,则在实例化Bean之前,将调用postProcessBeforeInstantiation()方法
2.根据配置文件的情况调用Bean构造函数或者工厂方法实例化Bean。
3.如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor,那么实例化Bean之后调用接口postProcessAfterInstantiation()方法,可以装载这里对已经实例化的对象进行一些"装饰"
4.如果Bean配置了属性信息,那么容器在这一步着手将配置文件中的属性值设置到对应的属性,设置每个属性之前先调用org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口的postProcessPropertyValue()方法。
5.调用Bean的属性设置方法设置属性
6.如果Bean实现了org.springframework.beans.factory.BeanNameAware接口,那么简化调用setBeanName()接口方法,将配置文件中搞Bean对应的名称设置到Bean中。
7.如果Bean实现了org.springframework.beans.factory.BeanFactoryAware接口,则将调用setBeanFactory()接口方法,将BeanFactory容器实例设置给Bean。
8.如果BeanFactory装配了org.springframework.beans.factory.config.BeanPostProcessor后处理器,则将调用
postProcessBeforeInitialization(Object bean, String beanName)接口方法对Bean进行加工操作,bean就是当前需要加工操作的Bean,而BeanName就是当前Bean的配置名。返回的对现货加工处理后的Bean。这个是实现AOP的重要基石。
9.如果Bean实现了org.springframework.beans.factory.InitializingBean接口则调用	void afterPropertiesSet() 方法
10.如果<bean>中荣光init-method属性定义了初始化方法,则将执行这个方法。
11.BeanPostProcessor后处理器定义了两个方法,一.Object postProcessBeforeInitialization(Object bean, String beanName)步骤8调用。二.Object postProcessAfterInitialization(Object bean, String beanName) ,此时调用它可以获得对Bean的再次加工。
12.作用域是prototype的将返回生成的bean给调用者,调用者负责该bean的生命周期,如果是singleton,那么则将Bean放入SpringIoC容器的缓冲池中,并且将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命周期的维护。
13.作用域是singleton的Bean,当容器关闭,如果实现DisposableBean接口,则将调用接口的destroy()方法,可以编写释放资源,记录日志等操作。
14.对于singleton的Bean,如果配置了destroy-method属性指定了Bean的销毁方法,那么会执行该方法释放资源。

代码实例链接: https://github.com/chenanddom/SpringSection4/tree/master/src/main/java/com/flexible/beanfactory/beanfactorylifecyle

#ApplicationContext中Bean的生命周期

猜你喜欢

转载自my.oschina.net/u/3474937/blog/2413588