
我接触spring自动注入的时间并不长,依稀记得有一点,一个类里可以使用自动注入其他bean的前提是这个类本身的管理也是要交给spring容器的。你调用这个方法所在的类可能并不是由spring来管理的,也就是说采用@Autowired这种自动注入应该是无效的,在针对这种情形,spring确实提供这样一种途径,就是在无法自动注入的情况下,直接调用beanfactory去拿某个bean的实例,调用这样方法得到的实例是跟自动注入得到的实例是一样的。
但是如果你主动去new这样一个bean,那spring容器是不会帮助你把这个bean里的属性初始化好的。
如果可以直接自动注入,谁不想省事一点呢,以上基本是我个人一点浅薄的项目经验,希望对你有帮助。
使用Spring MVC,配置DispatcherServlet是第一步。
DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet。
DispatcherServlet是前置控制器,配置在webxml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。
“某某规则”:是根据你使用了哪个HandlerMapping接口的实现类的不同而不同。
先来看第一个例子:
Xml代码
<load-on-startup>1</load-on-startup>是启动顺序,让这个Servlet随Servletp容器一起启动。
<url-pattern>form</url-pattern>会拦截form结尾的请求。
<servlet-name>example</servlet-name>这个Servlet的名字是example,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中,关于key,以后说明。
在DispatcherServlet的初始化过程中,框架会在web应用的WEB-INF文件夹下寻找名为[servlet-name]-servletxml的配置文件,生成文件中定义的bean。
ApplicationContext是Spring中的重要组件,它不是bean,因此无法通过getBean获取它,但是可以通过Autowired注入获得,其中必定有特殊的处理,本文将简述ApplicationContext及类似的BeanFactory是如何实现注入逻辑的最后稍微介绍一下Aware接口
普通Bean依赖Spring的容器功能进行注入,而ApplicationContext通过Spring的内建机制进行注入
在 查找依赖时 ,会同时搜寻beanDefinitionNames和resolvableDependencies,因此ApplicationContext也能被查找到
而 getBean时 只会查找上面的BeanDefinitionMap,因此找不到ApplicationContext
在AbstractApplicationContextprepareBeanFactory()中,ApplicationContext被注册到resolvableDependencies中
创建SimpleBean3时,Spring检测到有applicationContext和simpleBean2两个依赖,便会进行查找
在 SpringBoot中@Autowired是如何生效的 那篇文章中,讲到了带有@Autowired字段的在AutowiredAnnotationPostProcessorpostProcessProperties()中完成注入,查找依赖的入口就在 metadatainject(bean, beanName, pvs)
和注入相关的流程如下
我们直接跳到DefaultListableBeanFactoryfindAutowireCandidates(),可以看到是同时从BeanDefinitionNames和resolvableDependencies两个地方,因此可以找到ApplicationContext
Aware是一个标记接口,要求子接口需要提供一个回调setter方法使用者可以在恰当的时机被回调,举个例子,实现了ApplicationContextAware的实例会在某个时间触发setApplicationContext获得applicationContext的实例
那么直接@Autowired注入和使用ApplicationContextAware注入有什么区别呢
ApplicationContext不是bean,不能通过bean的方式进行注入,为了方面使用Spring通过特殊方式满足用户的注入需求,同时又提供了Aware系列接口,给与用户更大的自由度
Spring容器提供了一种管理方法,致力于解决我们各个层级之间的对象的调用关系。
我们通常调用各层级对象的时候,需要不断创建对象,一次访问就需要创建两个对象;如果我们使用Spring容器,将不同层级的对象放入容器中,每次使用的时候调用容器中的对象,就不用创建那么多对象,达到节约内存空间的目的。简单来讲,Spring容器就是存储JavaBean对象的容器。
BeanFactory是一个接口,需要创建继承的子类对象。下图是其继承结构图:
方式二:使用ClassPathApplicationContext获取容器类
这个方法替代了方式一,是我们创建容器类对象主要使用的方法。
方式三:使用FileSystemXmlApplicationContext来获取容器对象
这种方式在创建对象的时候需要传入配置文件的绝对路径,这个方法可以使用项目外部的配置文件
IOC(inversion of controller)指的是控制反转,简单来书,就是讲创建对象的过程或者创建对象的权限交给了spring框架来帮我们处理,我们不用再通过new的方式来创建JavaBean对象,这个过程就叫做IOC。
首先在applicationContext中配置我们的对象
然后从容器中获取我们的JavaBean对象
创建bean的工厂类
在配置文件中进行配置
获取JavaBean对象
创建工厂类的方式与上面相同
在配置文件中进行配置
获取JavaBean对象
Bean属性scope可以声明bean的作用域范围,Bean的作用域范围有四种:
我们可以在JavaBean类中添加init-method与destroy-method两个方法,实现bean在初始化和关闭的时候调用的方法,然后在配置文件中进行配置。
JavaBean类
配置文件
DI(dependency injection)指的是依赖注入,简单来说就是使用spring框架对我们的JavaBean对象赋值的过程。
使用上文中创建的JavaBean类
注意:一定要给属性生成对应的setter方法
配置JavaBean类的属性
从容器中获取我们的JavaBean对象
在JavaBean中生成带所有属性的构造方法
在配置文件中申明JavaBean对象
申明的另一种方法:
调用JavaBean的属性
P名称空间与C名称空间其实都是不存在的虚拟空间,主要是用于简化我们的spring为JavaBean属性赋值时的配置。
添加P名称空间与C名称空间到schema
通过setter方法进行赋值:
等价于:
通过构造方法进行赋值
等价于:
Spel表达式,类似于jstl与el表达式的语言,spring可以支持我们在为属性赋值的时候,通过spel表达式来进行更改我们的属性值。
创建一个含有集合属性的JavaBean类
为集合属性注入值
如果说BeanFactory是spring的心脏,那么Application就是完整的身躯。ApplicationContext就是由BeanFactory派生出来的。
1、ApplicationContext
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统加载文件。
如果配置文件放在类路径下,直接使用ClassPathXmlApplicationContext实现类:
ApplicationContext ctx=new ClassPathXmlApplicationContext("com/techman/context/beansxml");
这里的参数等同于:"classpath:com/techman/context/beansxml"
如果配置文件在文件系统的路径下,则可以优先考虑使用FileSystemXmlApplicationContext实现类:
ApplicationContext ctx=new FileSystemXmlApplicationContext("com/techman/context/beansxml");
这里的参数等同于:"file:com/techman/context/beansxml"
还可以指定一组配置文件,Spring自动将多个配置文件在内存中整合成一个配置文件:
ApplicationContext ctx=new ClassPathXmlApplicationContext(new String[]{"conf/bean1xml","conf/bean2xml"});
2、AnnotationConfigApplicationContext
直接实例:
[java] view plaincopyprint
package comtechmancontext;
import orgspringframeworkcontextannotationBean;
import orgspringframeworkcontextannotationConfiguration;
import comtechmanreflectCar;
@Configuration //表示是一个配置信息提供类,这里是通过类注解的配置方式
public class Beans
{
@Bean(name="car")
public Car buildCar()
{
Car car=new Car();
carsetBrand("红旗CA72");
carsetMaxSpeed(340);
return car;
}
}
复制代码
[java] view plaincopyprint
package comtechmancontext;
import orgspringframeworkcontextApplicationContext;
import orgspringframeworkcontextannotationAnnotationConfigApplicationContext;
import comtechmanreflectCar;
//这里需要spring-contextjar和spring-expressionjar的支持
public class AnnotationApplicationContext
{
public static void main(String []args)
{
//通过一个带@Configuration的POJO装载Bean配置
ApplicationContext ac=new AnnotationConfigApplicationContext(Beansclass);
Car car=acgetBean("car",Carclass);
carintroduce();
}
}
复制代码
AnnotationConfigApplicationContext将加载Beansclass中的Bean定义并调用Beansclass中实现的方法实例化Bean,启动容器并装配Bean
3、WebApplicationContext
WebApplicationContext是专门为Web应用准备的,它允许从相对于web根目录的路径中加载配置文件完成初始化工作。
WebApplicationContext扩展了ApplicationContext,WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,我们可以直接通过下面的语句从web容器中获取WebApplicationContext:
WebApplicationContext wac=(WebApplicationContext)servletContextgetAttribute(WebApplicationContextROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
4、ConfigurableWebApplicationContext
ConfigurableWebApplicationContext扩展了WebApplicationContext,它允许通过配置的方式实例化WebApplicationContext,它定义了两个重要的方法:
setServletContext(ServletContext servletContext):为Spring设置Web应用上下文,以便两者整合。
setConfigLocation(String[] configLocations)设置Spring配置文件地址,一般情况下,配置文件地址是相对于Web根目录的地址,如/WEB-INF/techman-daoxml等。也可以使用classpath:com/techman/context/techman-daoxml等格式。
5、Spring为使用WebApplicationContext的Servlet和Web容器监听器:
orgspringframeworkwebcontextContextLoaderServlet;
orgspringframeworkwebcontextContextLoaderListener;
这里是webxml启动WebApplicationContext的配置:
[html] view plaincopyprint
<!-- 从类路径下加载Spring配置文件,classpath关键字特指类路径下加载 ,如果多个文件则使用逗号或空格隔开-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContextxml,/WEB-INF/techman-daoxml</param-value>
</context-param>
<!-- 负责启动Spring容器的监听器,它将引用上面的上下文参数获得Spring配置文件地址 -->
<listener>
<listener-class>orgspringframeworkwebcontextContextLoaderListener</listener-class>
</listener>
复制代码
如果在不支持容器监听器的低版本Web容器中,我们可采用ContextLoaderServlet完成相同的工作:
[html] view plaincopyprint
<!-- 从类路径下加载Spring配置文件,classpath关键字特指类路径下加载 ,如果多个文件则使用逗号或空格隔开-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContextxml,/WEB-INF/techman-daoxml</param-value>
</context-param>
复制代码
[html] view plaincopyprint
<servlet>
<servlet-name>springContextLoaderServlet</servlet-name>
<servlet-class>orgspringframeworkwebcontextContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
复制代码
6、Log4j的配置
由于WebApplicationContext需要使用日志功能,用户可以将log4jproperties放置在类路径下,这样就会自动启动。如果放在其他地方,必须在webxml中指定Log4j配置文件的位置。Spring为启动Log4j引擎提供了两个类og4jConfigServlet和Log4jConfigListener,不管哪种方式都必须保证能够在装载Spring配置文件前先装载Log4J配置信息。
[html] view plaincopyprint
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4jproperties</param-value>
</context-param>
<servlet>
<servlet-name>log4jConfigServlet</servlet-name>
<servlet-class>orgspringframeworkwebutilLog4jConfigServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
复制代码
7、使用标注@Configuration的Java类提供配置信息
方式如下:
[html] view plaincopyprint
<!-- 通过指定context参数,让Spring使用AnnotationConfigWebApplicationContext而非XmlWebApplicationContext启动容器 -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
orgspringframeworkwebcontextsupportAnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- 指定标注了@Configuration的配置类,多个可以使用逗号或空格分隔 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>comsmartBeans,comsmartAppConfig2</param-value>
</context-param>
<!-- ContextLoaderListener监听器将根据上面的配置使用AnnotationConfigWebApplicationContext根据contextConfigLocation指定的配置类启动Spring容器 -->
<listener>
<listener-class>orgspringframeworkwebcontextContextLoaderListener</listener-class>
</listener>
众所周知,Context的含义是上下文,也就是场景的意思,关于获取全局Context的方法,一般情况是在自定义的Application中写一个,需要的时候直接获取就可以了,下面是代码所示:
private static Context context;
private static Context getContext(){
return context;
}
@Override
public void onCreate() {
superonCreate();
context=thisgetApplicationContext();
thisregisterActivityLifecycleCallbacks(this);
}
上面的代码是写在自定义的Application中的,以后我们需要获取全局Context的时候就用BaseApplicationgetContext()方法即可,请注意,上面的方法只是一般情况下的,就是我们已经进入了这个程序里面,但是就像刚才说的那样,如果不能接触程序逻辑,或者连Application都没有初始化的时候能获取吗,显然不能吧?那是不是就没有办法了?不是滴,经过查看源码,我们发现应用程序的入口里面就提供了获取全部Application的方法,这个类是ActivityThread,这个类也是大名鼎鼎的Android应用程序的主线程,它里面做的事情可多了呢,包括初始化消息循环机制等等,这也是为什么在主线程里面可以直接new
一个Handler的原因,而不用绑定线程的Looper,当然,本文讨论的问题不是Looper,而是全局Context对象,我们发现,ActivityThread有一个方法:
public Application getApplication() {
return mInitialApplication;
}
利用这个方法我们可以获取全局Application对象,当然也就可以获取全局Context对象啦,可问题是ActivityThread是一个隐藏类,怎么办呢,那就反射吧,下面是代码
Class<> clazz = ClassforName("androidappActivityThread");
Field field = clazzgetDeclaredField("sCurrentActivityThread");
fieldsetAccessible(true);
//得到ActivityThread的对象,虽然是隐藏的,但已经指向了内存的堆地址
Object object = fieldget(null);
Logd("[app]", "object=" + object);
Method method=clazzgetDeclaredMethod("getApplication");
methodsetAccessible(true);
Application application=(Application)methodinvoke(object);
Logd("[app]","application="+application);
Logd("[app]","程序的application对象="+getApplication());
测试结果如下:
可以看到成功获取了Application对象,那么获取Context就容易啦,而且我们看到,这样反射获取的跟直接程序调用的结果是一样的,其实这也很好理解,总之,这种方法一般情况下是在特殊的时候使用,比如注入的 *** 作可能使用到,好了,今天的博文就写到这里,有什么疑问提出来,其实ActivityThread还有一个静态方法直接获取application,获取的结果也是这个,都是一样的,大家去试试吧。需要补充的是,这个方法是无敌的,就像标题所说,任何地方,任何时候,任何逻辑,只要程序进入了主线程就能获取,而不是仅仅局限于网上说的在自定义的application中获取
以上就是关于Spring不用注入获取bean,这样获取applicationContext.getBean()有什么好处吗全部的内容,包括:Spring不用注入获取bean,这样获取applicationContext.getBean()有什么好处吗、dispatcherservlet怎么获取webapplicationcontext、ApplicationContext是如何被注入的等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)