Spring_1 博客分类: Spring Spring体系结构IoCjava的反射机制ApplicationContext
程序员文章站
2024-03-22 08:23:34
...
Spring是分层的Java SE/EE应用一站式的轻量级开源框架,以IoC(Inverse of Control)和AOP(Aspect Oriented Programming)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,此外,Spring整合了开源世界里众多的第三方框架和类库。
Spring的体系结构:
Spring整个框架按其所属功能可划分为5个主要模块:数据访问和集成、Web及远程操作、测试框架、AOP和IoC。
IoC:Spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理、获取等工作。BeanFactory接口是Spring框架的核心接口,它实现了容器许多核心的功能。Context模块构建于核心模块之上,扩展了BeanFactory的功能,添加了i18n国际化、Bean生命周期控制、框架事件体系、资源加载透明化等多项功能。此外,该模块还提供了许多企业级服务的支持。ApplicationContext是Context模块的核心接口。表达式语言模块是统一表达式语言的一个扩展,该表达式语言用于查询和管理运行期的对象,支持设置或获取对象属性,调用对象方法、操作数组、集合等。还提供了逻辑表达式运算、变量定义等功能。使用它可以方便地通过表达式串和Spring IoC容器进行交互。
AOP模块:AOP是进行横切逻辑编程的思想,开拓了人们考虑问题的思路。在AOP模块里,Spring提供了满足AOP Alliance规范的实现,此外,还整合了AspectJ这种AOP语言级的框架。Java 5.0引入java.lang.instrument,允许在JVM启动时启用一个代理类,通过该代理类在运行期修改类的字节码,改变一个类的功能,实现AOP的功能。
数据访问和集成:Spring站在DAO的抽象层面,建立了一套DAO层统一的异常体系,同时将各种访问数据的检查型异常转换成非检查型异常,为整个各种持久层框架提供基础。其次,Spring通过模版化技术对各种数据访问技术进行了薄层的封装,将模式化的代码隐藏起来,使数据访问的程序得到了大幅简化。
Web及远程调用:该模块建立在ApplicationContext模块之上,提供了Web应用的各种工具类,若通过Listener或Servlet初始化Spring容器,将Spring容器注册到Web容器中。其次,该模块还提供了多项面向Web的功能。此外,Spring还可以整合Struts、WebWork、Tapestry Web等MVC框架。
Spring注解:
在ApplicationContext文件中,使用Spring的<context:component-scan base-package="">扫描指定类包下的所有类,这样在类中定的Spring注解才能产生作用。
@Repository:定义一个DAO Bean
@Autowired:将Spring容器中的Bean注入进来
@Service:将一个类标注为一个服务层的Bean
@ContextConfiguration:指定Spring的配置容器
@Controller:将一个类标注为Spring MVC的Controller
@RequestMapping(value="/index.html"):负责处理一个xxx.html请求
IoC
DI(Dependency Injection):让调用类的某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移出调用类对某一接口实现类的的依赖。
从注入方法上来看,主要可以划分为3种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。
构造函数注入:在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入。
属性注入:属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入。
接口注入:将调用累所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。
java的反射机制
Java语言允许通过程序化的方式间接对Class的对象实例操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能.
例:
public Class Car{
private String brand;
private String color;
private int maxSpeed;
public Car(){}
public Car(String brand,String color,int maxSpeed){
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void introduce(){
System.out.println("brand"+brand+",color"+color+",+maxSpeed"+maxSpeed);
}
...
}
import java.lang.reflect.Construcor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest{
public static Car initByDefaultConst() throws Throwable{
//通过类加载器获取Car类对象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(Car):
//获取类的默认构造器对象并通过它实例化Car
Constructor cons = clazz.getDeclardConstructor((Class[])null);
Car car = (Car)cons.newInstance():
//通过反射方法设置属性
Method setBrand = clazz.getMethod("setBrand",String.class);
setBrand.invoke(car,"WCA72");
Method setColor = clazz.getMethod("setColor ",String.class);
setColor .invoke(car,"black");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed ",int.class);
setMaxSpeed .invoke(car,200);
return car;
}
public static void main(String[] args) throws Throwable{
Car car = initByDefaultConst();
car.introduce();
}
}
类装载器ClassLoader
工作机制:
类装载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件。在Java中,类装载器装入JVM中,要经过以下步骤:
1.装载:查找和导入Class文件
2.链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的:
校验:检查载入Class文件数据的正确性
准备:给类的静态变量分配存储空间
解析:将符号引用转成直接引用
3.初始化:对类的静态变量、静态代码块执行初始化工作
类加载器工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个ClassLoadre:根装载器、ExtClassLoader和AppClassLoader。根装载器不是ClassLoader的子类,负责装载JRE的核心类库。ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中,EctClassLoader负责装载JRE扩展目录ext中的JAR类包,AppClassLoader负责装载Classpath路径下的类包。
JVM装载类时使用“全盘负责委托机制”,“全盘负责”是指当一个ClassLoader装载一个类时,除非显式地使用另一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入:“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。
ClassLoader的重要方法:
Class loadClass(String name):name参数指定类装载器需要装载类的名字,必须使用全限定类名。该方法有一个重载方法loadClass(String name,boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,若JVM值需知道该类是否存在或找出该类的超类,那么就不需要进行解析。
Class defineClass(String name,byte[] b,int off,int len):将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。
Class findSystemClass(String name):从本地文件系统载入Class文件,若本地文件系统更不存在该Class文件,将抛出ClassNotFoundException异常。
Class findLoadedClass():调用该方法来查看ClassLoader是否已装入某个类。若已装入,则返回java.lang.Class对象,否则返回null。
ClassLoader getParent():获取类装载器的父装载器。
反射对象类在java.reflect包中定义,下面是最主要的三个反射类:
Constructor:类的构造函数反射类,通过Class#getContructors()方法可以获得类的所有构造函数反射对象数组。在JDK 5.0中,还可以通过getContructor(Class parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。
Method:类方法的反射类,通过Class#getDeclaredMehtods()方法可以获取类的所有方法反射类对象数组Method[]。在 JDK 5.0中可以通过getDeclaredMehtods(String name,Class parameterTypes)获取特定签名的方法,name为方法名;Class为方法入参类型列表。Method最主要的方法是invoke(Object obj,Object[] args),obj表示操作的目标对象,args为方法入参。
Method还有很多用于获取类方法更多信息的方法:
Class getReturnType():获取方法的返回值类型
Class[] getParameterTypes():获取方法的入参类型数组
Class[] getExceptionTypes():获取方法的一场类型数组
Annotationp[][] getParamerterAnnotations():获取方法的注解信息
Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredFields(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj,Object value),obj表示操作目标评对象,通过value为目标对象的成员变量设置值。若成员变为为基础类型,用户可以使用Field类中提供的带类型名的值设置方法。
通过反射机制可以调用私有变量和私有方法。但在访问private、protected成员变量和方法时必须通过setAccessible(boolean acess)方法取消java语言检查,否则抛出IllegalAccessException。若JVM的安全管理器设置了相应的安全机制,调用该方法将抛出SecurityException。
Spring设计了一个Resource接口,它为应用提供了更强的访问底层资源的能力。该接口拥有对应不同资源类型的实现类。Resource接口的主要方法:
boolean exists():资源是否存在
boolean isOpen():资源是否打开
URL getURL() throws IOException:若底层资源可以表示成URL,该方法返回对应的URL对象
File getFile() throws IOException:若底层资源对应一个文件,该方法返回对应的File对象
InputStream getInputStream() throws IOException:返回资源对应的输入流
Spring框架使用Resource装载各种资源,Resource的具体实现类如下:
ByteArrayResource:二进制数组表示的资源,二进制数组资源可以在内存中通过程序构造
ClassPathResource:类路径下的资源,资源以相对于类路径的方式表示
FileSystemResource:文件系统资源,资源以文件系统路径的方式表示
InputStreamResource:对应一个InputStream的资源
ServletContextResource:为访问web容器上下文中的资源而设计的类,负责以相对于Web应用根目录的路径加载资源,它支持已流和URL的方式访问,在WAR解包的情况下,也可以通过File的方式访问,还可以直接从JAR包中访问资源。
UrlResource:封装了java.net.URL,它使用户能够访问任何可以通过URL表示的资源。
对资源进行编码:
EncodedResource encRes = new EncodedResource(res,"UTF-8");
资源类型的地址前缀
地址前缀 示例
classpath classpath:com/beans.xml
对应资源类型:从类路径中加载资源,classpath:和classpath:/是等价的,都是相当于类的跟路径。资源文件可以在标准的文件系统中,也可以在jar或zip的类包中
file: file:/com/beans.xml
对应资源类型:使用UrlResource从文件系统目录中装载资源,可采用绝对或相对路径
http:// http://www.beans.xml
对应资源类型:使用UrlResource从Web服务器中装载资源
ftp:// ftp://www.beans.xml
对应资源类型:使用UrlResource从FTP服务器中装载资源
没有前缀 com/beans.xml
对应资源类型:根据ApplicationContext具体实现类采用对应的类型的Resource
Ant风格资源地址支持3种匹配符:
?:匹配文件名中的一个字符
*:匹配文件名中任意字符
**:匹配多层路径
Spring定义一套资源加载的接口,并提供了实现类。ResourceLoader接口仅有一个getResource(String location)的方法,可以根据一个资源地址加载文件资源。不过这个文件资源仅支持带资源类型前缀的表达式,不支持Ant风格的资源路径表达式。ResourcePatternResolver扩展了ResourceLoader接口,定义了一个新的接口方法:getResources(String locationPattern),该方法支持带资源类型前缀及Ant风格的资源路径的表达式。PathMatchingResourcePatternResolver是Spring提供了标准实现类。
Spring为BeanFactory提供了多种实现,最常用的XmlBeanFactory。
BeanFactory最主要的方法就是getBean(String beanName),该方法从容器中返回特定该名称的Bean。BeanFactory的其他接口:
ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法
HierarchicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口方法访问父容器。
ConfigurableBeanFactory:增强IoC容器的可定制性,它定义了设置类装载其、属性编辑器、容器初始化后置处理器等方法
AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配的方法
SingletonBeanRegistry:定义了允许在运行期间向容器注册单实例Bean的方法
BeanDefinitionRegistry:Spring配置文件中每一个<bean>节点元素在Spring容器里都通过一个BeanDefinition对象表示,他描述了Bean的配置信息。而BeanDefinition Registry接口提供了向容器手工注册BeanDefinition对象的方法。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。ApplicationContext继承了HierarchicalBeanFactory和ListableBeanFactory接口。
ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了ApplicationListener事件监听接口的Bean可以接受到容器事件,并对事件进行响应处理。在ApplicationContext抽象实现类AbstractApplicationContext中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者。
MessageSource:为应用提供il18n国际化消息访问功能。
ResourcePatternResolver:所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver功能,可以通过带前缀的Ant风格的资源文件路径装载Spring的配置文件。
LifeCycle:该接口提供了start()和stop()两个方法,主要用于控制异步处理过程。在具体使用时,ApplicationContext及具体的Bean都必须同时实现该接口,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,已达到管理和控制JMX、任务调度等目的。
ConfigurableApplicationContext扩展与ApplicationContext,它新增了refresh()和close(),让ApplicationContext具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下,调用refresh()则清理缓存并重新装载配置信息,而调用close()则可关闭应用上下文。
ApplicationContext和BeanFactory的重大区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因次,ApplicationContext的初始化事件会比BeanFactory稍长,但之后的调用没有第一次惩罚的问题。
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用程序可以访问Srping应用上下文。Spring专门为次提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,既可以从ServletContext中获取WebApplicationContext是实例。在WebApplicationContext中还为Bean添加了三个新的作用域:request作用域、session作用域和global session作用域。而在为Web应用环境下,Bean只有singleton和prototype两种作用域。
WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中。
ConfigurableApplicationContext允许通过配置的方式实例化WebApplicationContext。
setServletContext(ServletContext servletContext):为Spring设置Web应用上下文,以便两者整合
setConfigLocations(String[] configLocations):设置Spring配置文件地址,一般情况下,配置文件地址是相对于Web根目录的地址。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别。WebApplicationContext秀奥ServletContext实例,也就是说它必须在拥有Web容器的前提下才能完成启动的工作。
Spring分别提供了用于启动WebApplicationContext的Servlet的Web容器监听器:
org.springframework.web.context.ContextLoaderServlet、org.springframework.web.context.ContextLoaderListener两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两只之一,并在web.xml中完成配置就可以了。
由于WebApplicationContext需要使用日志功能,用户可以将Log4J的配置文件放置到类路径的WEB-INF/classes下,这时Log4J引擎即可顺利启动。Spring为启动Log4J引擎提供了两个类似于启动WebApplicationContext的实现类:Log4jConfigServlet和Log4jConfigListener。
<context-param>
<param-name>contextConfigLocation</param-name>
<paramm-value>
/WEB-INF/xxx.xml,/WEB-INF/xxxx.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<paramm-value>
/WEB-INF/log4j.properties
</param-value>
</context-param>
<servlet>
<servlet-name>log4jConfigServlet</servlet-name>
<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
通过HierarchicalBeanFactory接口,Spring的IoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的bean。父子容器层级体系增强了Spring容器架构的扩展性和灵活性。
Bean的生命周期:
1.当调用者通过getBean(beanName)向容器请求某一个Bean时,若容器注册了org.springframework.beans.factory.InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法。
2.根据配置情况调用Bean构造函数或工厂方法实例化Bean
3.若容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessAfterInstantiation()方法,可在这里对已经实例化的对象进行相关操作。
4.若Bean配置了属性信息,容器在这一步着手将配置值设置到Bean对应的属性中,不过在设置每个属性之前先将调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法。
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后处理器,将调用BeanPostProcessor的Object postProcessBeforeInitialization(Object bean,String beanName)接口方法对Bean进行加工操作。其中参数bean是当前正在处理的bean,而beanName的hi当前Bean的配置名,返回的对象为加工处理后的Bean。BeanPostProcessor在Spring框架中占有重要地位,为容器提供对Bean进行后加工处理的切入点。
9.若Bean实现了InitializingBean的接口,将调用接口的afterPropertiesSet()方法
10.若在<bean>通过init-method属性定义了初始化方法,将执行这个方法
11.BeanPostProcessor后处理器定义了两个方法:其一是postProcessBeforeInitializatiopn()在第8步调用;其二是Object postProcessAfterInitialization(Object bean,String beanName)方法,这个方法在此时调用,容器在此获得对Bean进行加工处理的机会。
12.若在<bean>中指定Bean的作用范围为scope='prototype',将Bean返回给调用者,调用者负责调用者后续生命的管理,Spring不再管理这个Bean的生命周期。若作用范围设置为scope='singleton',则将Bean放入到Spring IoC容器的缓存池中,并将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命管理。
13.对于scope='singleton'的Bean,当容器关闭时,将触发Spring对Bean的后续生命周期的管理工作,首先,若Bean实现了DisposableBean接口,则将调用接口的afterPropertiesSet()方法,可以在次编写释放资源、记录日志等操作。
14.对于scope='singleton'的Bean,若通过<bean>的destroy-method属性指定了Bean的销毁方法,Spring将执行Bean这个方法,完成Bean资源的释放等操作。
Bean的完整生命周期从Spring容器着手实例化Bean开始,知道最终销毁Bean,这当中经过了许多关键点,每个关键点都涉及特定的方法调用,可以将这些方法大致划分为三类:
Bean自身的方法:若调用Bean构造函数实例化Bean,调用setter设置Bean的属性值以及通过<bean>的init-method和destroy-method所制定的方法;
Bean级生命周期接口方法:如BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean直接实现
容器级生命周期接口方法:后处理器接口一般不由Bean本身实现,他们独立于Bean,实现类以容器附加装置的形式注册到Spring容器中并通过接口反射为Spring容器预先识别。Spring容器创建Bean时,这些后处理器都会发生作用,所以这些后处理器的影响是全局的。
ApplicationContext和BeanFactory的最大区别在于前者会利用Java的反射机制自动识别出配置文件中定义的BeanPostProcessor、IntantiationAwareBeanPostProcessor和BeanFactoryProcessor,并将他们注册到应用上下文中,而后者需要在代码中通过手工调用addBeanPostProcessor()方法进行注册。
Spring的体系结构:
Spring整个框架按其所属功能可划分为5个主要模块:数据访问和集成、Web及远程操作、测试框架、AOP和IoC。
IoC:Spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理、获取等工作。BeanFactory接口是Spring框架的核心接口,它实现了容器许多核心的功能。Context模块构建于核心模块之上,扩展了BeanFactory的功能,添加了i18n国际化、Bean生命周期控制、框架事件体系、资源加载透明化等多项功能。此外,该模块还提供了许多企业级服务的支持。ApplicationContext是Context模块的核心接口。表达式语言模块是统一表达式语言的一个扩展,该表达式语言用于查询和管理运行期的对象,支持设置或获取对象属性,调用对象方法、操作数组、集合等。还提供了逻辑表达式运算、变量定义等功能。使用它可以方便地通过表达式串和Spring IoC容器进行交互。
AOP模块:AOP是进行横切逻辑编程的思想,开拓了人们考虑问题的思路。在AOP模块里,Spring提供了满足AOP Alliance规范的实现,此外,还整合了AspectJ这种AOP语言级的框架。Java 5.0引入java.lang.instrument,允许在JVM启动时启用一个代理类,通过该代理类在运行期修改类的字节码,改变一个类的功能,实现AOP的功能。
数据访问和集成:Spring站在DAO的抽象层面,建立了一套DAO层统一的异常体系,同时将各种访问数据的检查型异常转换成非检查型异常,为整个各种持久层框架提供基础。其次,Spring通过模版化技术对各种数据访问技术进行了薄层的封装,将模式化的代码隐藏起来,使数据访问的程序得到了大幅简化。
Web及远程调用:该模块建立在ApplicationContext模块之上,提供了Web应用的各种工具类,若通过Listener或Servlet初始化Spring容器,将Spring容器注册到Web容器中。其次,该模块还提供了多项面向Web的功能。此外,Spring还可以整合Struts、WebWork、Tapestry Web等MVC框架。
Spring注解:
在ApplicationContext文件中,使用Spring的<context:component-scan base-package="">扫描指定类包下的所有类,这样在类中定的Spring注解才能产生作用。
@Repository:定义一个DAO Bean
@Autowired:将Spring容器中的Bean注入进来
@Service:将一个类标注为一个服务层的Bean
@ContextConfiguration:指定Spring的配置容器
@Controller:将一个类标注为Spring MVC的Controller
@RequestMapping(value="/index.html"):负责处理一个xxx.html请求
IoC
DI(Dependency Injection):让调用类的某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移出调用类对某一接口实现类的的依赖。
从注入方法上来看,主要可以划分为3种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。
构造函数注入:在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入。
属性注入:属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入。
接口注入:将调用累所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。
java的反射机制
Java语言允许通过程序化的方式间接对Class的对象实例操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能.
例:
public Class Car{
private String brand;
private String color;
private int maxSpeed;
public Car(){}
public Car(String brand,String color,int maxSpeed){
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void introduce(){
System.out.println("brand"+brand+",color"+color+",+maxSpeed"+maxSpeed);
}
...
}
import java.lang.reflect.Construcor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest{
public static Car initByDefaultConst() throws Throwable{
//通过类加载器获取Car类对象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(Car):
//获取类的默认构造器对象并通过它实例化Car
Constructor cons = clazz.getDeclardConstructor((Class[])null);
Car car = (Car)cons.newInstance():
//通过反射方法设置属性
Method setBrand = clazz.getMethod("setBrand",String.class);
setBrand.invoke(car,"WCA72");
Method setColor = clazz.getMethod("setColor ",String.class);
setColor .invoke(car,"black");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed ",int.class);
setMaxSpeed .invoke(car,200);
return car;
}
public static void main(String[] args) throws Throwable{
Car car = initByDefaultConst();
car.introduce();
}
}
类装载器ClassLoader
工作机制:
类装载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件。在Java中,类装载器装入JVM中,要经过以下步骤:
1.装载:查找和导入Class文件
2.链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的:
校验:检查载入Class文件数据的正确性
准备:给类的静态变量分配存储空间
解析:将符号引用转成直接引用
3.初始化:对类的静态变量、静态代码块执行初始化工作
类加载器工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生三个ClassLoadre:根装载器、ExtClassLoader和AppClassLoader。根装载器不是ClassLoader的子类,负责装载JRE的核心类库。ExtClassLoader和AppClassLoader都是ClassLoader的子类。其中,EctClassLoader负责装载JRE扩展目录ext中的JAR类包,AppClassLoader负责装载Classpath路径下的类包。
JVM装载类时使用“全盘负责委托机制”,“全盘负责”是指当一个ClassLoader装载一个类时,除非显式地使用另一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入:“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。
ClassLoader的重要方法:
Class loadClass(String name):name参数指定类装载器需要装载类的名字,必须使用全限定类名。该方法有一个重载方法loadClass(String name,boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,若JVM值需知道该类是否存在或找出该类的超类,那么就不需要进行解析。
Class defineClass(String name,byte[] b,int off,int len):将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。
Class findSystemClass(String name):从本地文件系统载入Class文件,若本地文件系统更不存在该Class文件,将抛出ClassNotFoundException异常。
Class findLoadedClass():调用该方法来查看ClassLoader是否已装入某个类。若已装入,则返回java.lang.Class对象,否则返回null。
ClassLoader getParent():获取类装载器的父装载器。
反射对象类在java.reflect包中定义,下面是最主要的三个反射类:
Constructor:类的构造函数反射类,通过Class#getContructors()方法可以获得类的所有构造函数反射对象数组。在JDK 5.0中,还可以通过getContructor(Class parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是newInstance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。
Method:类方法的反射类,通过Class#getDeclaredMehtods()方法可以获取类的所有方法反射类对象数组Method[]。在 JDK 5.0中可以通过getDeclaredMehtods(String name,Class parameterTypes)获取特定签名的方法,name为方法名;Class为方法入参类型列表。Method最主要的方法是invoke(Object obj,Object[] args),obj表示操作的目标对象,args为方法入参。
Method还有很多用于获取类方法更多信息的方法:
Class getReturnType():获取方法的返回值类型
Class[] getParameterTypes():获取方法的入参类型数组
Class[] getExceptionTypes():获取方法的一场类型数组
Annotationp[][] getParamerterAnnotations():获取方法的注解信息
Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredFields(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj,Object value),obj表示操作目标评对象,通过value为目标对象的成员变量设置值。若成员变为为基础类型,用户可以使用Field类中提供的带类型名的值设置方法。
通过反射机制可以调用私有变量和私有方法。但在访问private、protected成员变量和方法时必须通过setAccessible(boolean acess)方法取消java语言检查,否则抛出IllegalAccessException。若JVM的安全管理器设置了相应的安全机制,调用该方法将抛出SecurityException。
Spring设计了一个Resource接口,它为应用提供了更强的访问底层资源的能力。该接口拥有对应不同资源类型的实现类。Resource接口的主要方法:
boolean exists():资源是否存在
boolean isOpen():资源是否打开
URL getURL() throws IOException:若底层资源可以表示成URL,该方法返回对应的URL对象
File getFile() throws IOException:若底层资源对应一个文件,该方法返回对应的File对象
InputStream getInputStream() throws IOException:返回资源对应的输入流
Spring框架使用Resource装载各种资源,Resource的具体实现类如下:
ByteArrayResource:二进制数组表示的资源,二进制数组资源可以在内存中通过程序构造
ClassPathResource:类路径下的资源,资源以相对于类路径的方式表示
FileSystemResource:文件系统资源,资源以文件系统路径的方式表示
InputStreamResource:对应一个InputStream的资源
ServletContextResource:为访问web容器上下文中的资源而设计的类,负责以相对于Web应用根目录的路径加载资源,它支持已流和URL的方式访问,在WAR解包的情况下,也可以通过File的方式访问,还可以直接从JAR包中访问资源。
UrlResource:封装了java.net.URL,它使用户能够访问任何可以通过URL表示的资源。
对资源进行编码:
EncodedResource encRes = new EncodedResource(res,"UTF-8");
资源类型的地址前缀
地址前缀 示例
classpath classpath:com/beans.xml
对应资源类型:从类路径中加载资源,classpath:和classpath:/是等价的,都是相当于类的跟路径。资源文件可以在标准的文件系统中,也可以在jar或zip的类包中
file: file:/com/beans.xml
对应资源类型:使用UrlResource从文件系统目录中装载资源,可采用绝对或相对路径
http:// http://www.beans.xml
对应资源类型:使用UrlResource从Web服务器中装载资源
ftp:// ftp://www.beans.xml
对应资源类型:使用UrlResource从FTP服务器中装载资源
没有前缀 com/beans.xml
对应资源类型:根据ApplicationContext具体实现类采用对应的类型的Resource
Ant风格资源地址支持3种匹配符:
?:匹配文件名中的一个字符
*:匹配文件名中任意字符
**:匹配多层路径
Spring定义一套资源加载的接口,并提供了实现类。ResourceLoader接口仅有一个getResource(String location)的方法,可以根据一个资源地址加载文件资源。不过这个文件资源仅支持带资源类型前缀的表达式,不支持Ant风格的资源路径表达式。ResourcePatternResolver扩展了ResourceLoader接口,定义了一个新的接口方法:getResources(String locationPattern),该方法支持带资源类型前缀及Ant风格的资源路径的表达式。PathMatchingResourcePatternResolver是Spring提供了标准实现类。
Spring为BeanFactory提供了多种实现,最常用的XmlBeanFactory。
BeanFactory最主要的方法就是getBean(String beanName),该方法从容器中返回特定该名称的Bean。BeanFactory的其他接口:
ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法
HierarchicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口方法访问父容器。
ConfigurableBeanFactory:增强IoC容器的可定制性,它定义了设置类装载其、属性编辑器、容器初始化后置处理器等方法
AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则进行自动装配的方法
SingletonBeanRegistry:定义了允许在运行期间向容器注册单实例Bean的方法
BeanDefinitionRegistry:Spring配置文件中每一个<bean>节点元素在Spring容器里都通过一个BeanDefinition对象表示,他描述了Bean的配置信息。而BeanDefinition Registry接口提供了向容器手工注册BeanDefinition对象的方法。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。ApplicationContext继承了HierarchicalBeanFactory和ListableBeanFactory接口。
ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了ApplicationListener事件监听接口的Bean可以接受到容器事件,并对事件进行响应处理。在ApplicationContext抽象实现类AbstractApplicationContext中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者。
MessageSource:为应用提供il18n国际化消息访问功能。
ResourcePatternResolver:所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver功能,可以通过带前缀的Ant风格的资源文件路径装载Spring的配置文件。
LifeCycle:该接口提供了start()和stop()两个方法,主要用于控制异步处理过程。在具体使用时,ApplicationContext及具体的Bean都必须同时实现该接口,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,已达到管理和控制JMX、任务调度等目的。
ConfigurableApplicationContext扩展与ApplicationContext,它新增了refresh()和close(),让ApplicationContext具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下,调用refresh()则清理缓存并重新装载配置信息,而调用close()则可关闭应用上下文。
ApplicationContext和BeanFactory的重大区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因次,ApplicationContext的初始化事件会比BeanFactory稍长,但之后的调用没有第一次惩罚的问题。
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用程序可以访问Srping应用上下文。Spring专门为次提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,既可以从ServletContext中获取WebApplicationContext是实例。在WebApplicationContext中还为Bean添加了三个新的作用域:request作用域、session作用域和global session作用域。而在为Web应用环境下,Bean只有singleton和prototype两种作用域。
WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中。
ConfigurableApplicationContext允许通过配置的方式实例化WebApplicationContext。
setServletContext(ServletContext servletContext):为Spring设置Web应用上下文,以便两者整合
setConfigLocations(String[] configLocations):设置Spring配置文件地址,一般情况下,配置文件地址是相对于Web根目录的地址。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别。WebApplicationContext秀奥ServletContext实例,也就是说它必须在拥有Web容器的前提下才能完成启动的工作。
Spring分别提供了用于启动WebApplicationContext的Servlet的Web容器监听器:
org.springframework.web.context.ContextLoaderServlet、org.springframework.web.context.ContextLoaderListener两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两只之一,并在web.xml中完成配置就可以了。
由于WebApplicationContext需要使用日志功能,用户可以将Log4J的配置文件放置到类路径的WEB-INF/classes下,这时Log4J引擎即可顺利启动。Spring为启动Log4J引擎提供了两个类似于启动WebApplicationContext的实现类:Log4jConfigServlet和Log4jConfigListener。
<context-param>
<param-name>contextConfigLocation</param-name>
<paramm-value>
/WEB-INF/xxx.xml,/WEB-INF/xxxx.xml
</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLocation</param-name>
<paramm-value>
/WEB-INF/log4j.properties
</param-value>
</context-param>
<servlet>
<servlet-name>log4jConfigServlet</servlet-name>
<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
通过HierarchicalBeanFactory接口,Spring的IoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的bean。父子容器层级体系增强了Spring容器架构的扩展性和灵活性。
Bean的生命周期:
1.当调用者通过getBean(beanName)向容器请求某一个Bean时,若容器注册了org.springframework.beans.factory.InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法。
2.根据配置情况调用Bean构造函数或工厂方法实例化Bean
3.若容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessAfterInstantiation()方法,可在这里对已经实例化的对象进行相关操作。
4.若Bean配置了属性信息,容器在这一步着手将配置值设置到Bean对应的属性中,不过在设置每个属性之前先将调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法。
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后处理器,将调用BeanPostProcessor的Object postProcessBeforeInitialization(Object bean,String beanName)接口方法对Bean进行加工操作。其中参数bean是当前正在处理的bean,而beanName的hi当前Bean的配置名,返回的对象为加工处理后的Bean。BeanPostProcessor在Spring框架中占有重要地位,为容器提供对Bean进行后加工处理的切入点。
9.若Bean实现了InitializingBean的接口,将调用接口的afterPropertiesSet()方法
10.若在<bean>通过init-method属性定义了初始化方法,将执行这个方法
11.BeanPostProcessor后处理器定义了两个方法:其一是postProcessBeforeInitializatiopn()在第8步调用;其二是Object postProcessAfterInitialization(Object bean,String beanName)方法,这个方法在此时调用,容器在此获得对Bean进行加工处理的机会。
12.若在<bean>中指定Bean的作用范围为scope='prototype',将Bean返回给调用者,调用者负责调用者后续生命的管理,Spring不再管理这个Bean的生命周期。若作用范围设置为scope='singleton',则将Bean放入到Spring IoC容器的缓存池中,并将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命管理。
13.对于scope='singleton'的Bean,当容器关闭时,将触发Spring对Bean的后续生命周期的管理工作,首先,若Bean实现了DisposableBean接口,则将调用接口的afterPropertiesSet()方法,可以在次编写释放资源、记录日志等操作。
14.对于scope='singleton'的Bean,若通过<bean>的destroy-method属性指定了Bean的销毁方法,Spring将执行Bean这个方法,完成Bean资源的释放等操作。
Bean的完整生命周期从Spring容器着手实例化Bean开始,知道最终销毁Bean,这当中经过了许多关键点,每个关键点都涉及特定的方法调用,可以将这些方法大致划分为三类:
Bean自身的方法:若调用Bean构造函数实例化Bean,调用setter设置Bean的属性值以及通过<bean>的init-method和destroy-method所制定的方法;
Bean级生命周期接口方法:如BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean直接实现
容器级生命周期接口方法:后处理器接口一般不由Bean本身实现,他们独立于Bean,实现类以容器附加装置的形式注册到Spring容器中并通过接口反射为Spring容器预先识别。Spring容器创建Bean时,这些后处理器都会发生作用,所以这些后处理器的影响是全局的。
ApplicationContext和BeanFactory的最大区别在于前者会利用Java的反射机制自动识别出配置文件中定义的BeanPostProcessor、IntantiationAwareBeanPostProcessor和BeanFactoryProcessor,并将他们注册到应用上下文中,而后者需要在代码中通过手工调用addBeanPostProcessor()方法进行注册。