Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他们都可代表Spring容器,Spring容器是生成Bean实例的工厂,并且管理容器中的Bean。
Bean是Spring管理的基本单位,在基于Spring的Java EE应用中,所有的组件都被当成Bean处理,包括数据源、Hibernate的SessionFactory、事务管理器等。在Spring中,Bean的是一个非常广义的概念,任何的Java对象、Java组件都被当成Bean处理。
而且应用中的所有组件,都处于Spring的管理下,都被Spring以Bean的方式管理,Spring负责创建Bean实例,并管理他们的生命周期。Bean在Spring容器中运行,无须感受Spring容器的存在,一样可以接受Spring的依赖注入,包括Bean属性的注入,协作者的注入、依赖关系的注入等。
Spring容器负责创建Bean实例,所以需要知道每个Bean的实现类,Java程序面向接口编程,无须关心Bean实例的实现类;但是Spring容器必须能够精确知道每个Bean实例的实现类,因此Spring配置文件必须精确配置Bean实例的实现类。
一、Spring容器
Spring容器最基本的接口就是BeanFactor。BeanFactory负责配置、创建、管理Bean,他有一个子接口:ApplicationContext,因此也称之为Spring上下文。Spring容器负责管理Bean与Bean之间的依赖关系。
BeanFactory接口包含以下几个基本方法:
Ø Boolean containBean(String name):判断Spring容器是否包含id为name的Bean实例。
Ø <T> getBean(Class<T> requiredTypr):获取Spring容器中属于requiredType类型的唯一的Bean实例。
Ø Object getBean(String name):返回Sprin容器中id为name的Bean实例。
Ø <T> T getBean(String name,Class requiredType):返回容器中id为name,并且类型为requiredType的Bean
Ø Class <?> getType(String name):返回容器中指定Bean实例的类型。
调用者只需使用getBean()方法即可获得指定Bean的引用,无须关心Bean的实例化过程。即Bean实例的创建过程完全透明。
在使用BeanFactory接口时,我们一般都是使用这个实现类:org.springframework.beans.factory.xml.XmlBeanFactory。然而ApplicationContext作为BeanFactory的子接口,使用它作为Spring容器会更加方便。它的实现类有:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext、AnnotationConfigApplicationContext。
创建Spring容器实例时,必须提供Spring容器管理的Bean的详细配置信息。Spring的配置信息通常采用xml配置文件来设置,因此,创建BeanFactory实例时,应该提供XML配置文件作为参数。
web.xml中的配置:
<!--设置Spring的配置文件启动路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/app-context.xml</param-value>
</context-param>
<!-- Spring启动监听器 -->
<listener>
<listener-class>com.hnjl.core.web.listener.StartupListner</listener-class>
</listener>
在这里我们首先讲解下web项目启动的加载顺序:
以Tomcat举例,启动Tomcat之后,首先会加载web.xml文件:
1、容器首先读取web.xml中的的配置内容和标签中配置项;
2、紧接着实例化ServletContext对象,并将配置的内容转化为键值传递给ServletContext;
3、创建配置的监听器的类实例,并且启动监听;
4、调用listener的contextInitialized(ServletContextEvent args)方法,ServletContext= ServletContextEvent.getServletContext();
此时你可以通过ServletContext获取context-param配置的内容并可以加以修改,此时Tomcat还没完全启动完成。
5、后续加载配置的各类filter;
6、最后加载servlet;
最后的结论是:web.xml中配置项的加载顺序是context-param=>listener=>filter=>servlet,配置项的顺序并不会改变加载顺序,但是同类型的配置项会应该加载顺序,servlet中也可以通过load-on-startup来指定加载顺序
接下来创建一个自己的工具类(AppUtil)实现Spring的ApplicationContextAware接口。最后在Spring配置文件中注册你的工具类。配置如下:
<!-- spring上下文,可以获取 上下文的Context -->
<bean id="appUtil" class="com.hnjl.core.util.AppUtil" />
AppUtil的实现代码:
/**
* 获取ApplicationContext, 调用方法 AppUtil.getContext();<br>
* 在spring文件中配置方法:<br>
* <bean id="AppUtil" class="com.hnjl.core.util.AppUtil" />
* @author hnjl
*
*/
public class AppUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 在线用户
*/
private static Map<Long,OnlineUser> onlineUsers=new LinkedHashMap<Long, OnlineUser>();
private static ServletContext servletContext;
/**
*
* @param servletContext
*/
public static void init(ServletContext _servletContext)
{
servletContext=_servletContext;
}
/**
* 获取web应用的ServletContext对象。
* @return
* @throws Exception
*/
public static ServletContext getServletContext() throws Exception{
return servletContext;
}
/**
* spring启动时注入context
*/
public void setApplicationContext(ApplicationContext contex) throws BeansException {
applicationContext=contex;
}
/**
* 获取spring的上下文。
* @return
*/
public static ApplicationContext getContext(){
return applicationContext;
}
public static List<Class> getImplClass(Class clazz) throws ClassNotFoundException{
List<Class> list=new ArrayList<Class>();
Map map= applicationContext.getBeansOfType(clazz);
for(Object obj : map.values()){
String name=obj.getClass().getName();
int pos=name.indexOf("$$");
if(pos>0){
name=name.substring(0,name.indexOf("$$")) ;
}
Class cls= Class.forName(name);
list.add(cls);
}
return list;
}
/**
* 根据类从spring上下文获取bean。
* @param cls
* @return
*/
public static <C> C getBean(Class<C> cls){
return applicationContext.getBean(cls);
}
/**
* 根据类名从spring上下文获取bean。
* @param cls
* @return
*/
public static Object getBean(String beanId){
return applicationContext.getBean(beanId);
}
/**
* 取得应用程序的绝对路径
* @return
*/
public static String getAppAbsolutePath(){
return servletContext.getRealPath("/");
}
/**
* 在web环境中根据web页面的路径获取对应页面的绝对路径。
* @param path
* @return
*/
public static String getRealPath(String path){
return servletContext.getRealPath(path);
}
public static Map<Long, OnlineUser> getOnlineUsers() {
return onlineUsers;
}
/**
* 获取Classpath物理路径
* @return
*/
public static String getClasspath(){
String classPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String rootPath = "";
//windows下
if("\\".equals(File.separator)){
rootPath = classPath.substring(1);
rootPath = rootPath.replace("/", "\\");
}
//linux下
if("/".equals(File.separator)){
rootPath = classPath.substring(1);
rootPath = rootPath.replace("\\", "/");
}
return rootPath;
}
/**
* Spring发布事件。
* @param applicationEvent
*/
public static void publishEvent(ApplicationEvent applicationEvent){
applicationContext.publishEvent(applicationEvent);
}
spring启动监听器实现代码:
public class StartupListner extends ContextLoaderListener {
@Override
public void contextInitialized(ServletContextEvent event) {
super.contextInitialized(event);
AppUtil.init(event.getServletContext());
}
}