1.Bean后处理器

Spring容器提供了一个接口InitializingBean,实现这个接口的bean只要重写afterPropertiesSet()或者在XML中添加init-method属性,就可以在Bean初始化前后执行特定行为。

InitializingBean是针对单个Bean起作用的,Spring还提供了另外一个接口叫BeanPostProcessor,这个接口是针对容器中所有Bean起作用的。

只要定义个普通的Bean实现这个接口,并实现postProcessBeforeInitialization()和postProcessAfterInitialization()两个方法,容器中所有的Bean都会受影响。

下面是一个实现了BeanPostProcessor的普通bean类,

 package spi;

 import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor { @Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("Bean后处理器在初始化之前对 "+beanName+" 进行增强处理...");
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("Bean后处理器在初始化之后对 "+beanName+" 进行增强处理...");
if (bean instanceof Chinese) {
System.out.println("Bean后处理器将Chinese.beanName修改为'Java编程'.");
Chinese c = (Chinese)bean;
c.setBeanName("Java编程");
}
return bean;
} }

特意让这个bean修改chinese这个bean的属性,下面是chinese Bean的代码,

 package spi;

 import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean; public class Chinese implements Person, InitializingBean {
private Son son;
private String beanName; public void setBeanName(String beanName) {
System.out.println(beanName+":Spring正在执行setBeanName()方法注入依赖关系...");
this.beanName = beanName; }
public void info(){
System.out.println("我在XML中的id名称为:"+beanName);
}
public int age;
private Axe axe;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Axe getAxe() {
return axe;
}
public void setAxe(Axe axe) {
this.axe = axe;
}
public Chinese() {
System.out.println("Spring正在执行默认构造函数构造Chinese实例...");
}
public Chinese(Axe axe) {
this.axe = axe;
}
public void useAxe() {
System.out.println("我打算去砍点柴火");
System.out.println("修改beanName="+beanName+","+axe.chop());
}
public void close() {
System.out.println("正在执行销毁前的方法 close ...");
}
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
public void init() {
System.out.println("正在执行初始化方法init...");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("正在执行初始化方法afterPropertiesSet...");
} }

Chinese Bean实现了InitializingBean接口,添加了init()(需要XML配置)方法和afterPropertiesSet()方法,那么它在初始化前后会被插入特定行为,行为由这两个方法决定。 同时,由于容器中存在实现了BeanPostProcessor的Bean,那么所有Bean包括chinese,都会在初始化前后批量被插入特定行为。

XML配置如下,

     <bean id="chinese" class="spi.Chinese"
init-method="init" p:axe-ref="steelAxe" p:beanName="依赖注入的值" />
<bean class="spi.MyBeanPostProcessor" />

测试代码,

     public static void test8() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("chinese", Person.class);
p.useAxe();
}

执行结果,

 Bean后处理器在初始化之前对 messageSource 进行增强处理...
Bean后处理器在初始化之后对 messageSource 进行增强处理...
Bean后处理器在初始化之前对 stoneAxe 进行增强处理...
Bean后处理器在初始化之后对 stoneAxe 进行增强处理...
Bean后处理器在初始化之前对 steelAxe 进行增强处理...
Bean后处理器在初始化之后对 steelAxe 进行增强处理...
Bean后处理器在初始化之前对 spi.EmailNotifier#0 进行增强处理...
Bean后处理器在初始化之后对 spi.EmailNotifier#0 进行增强处理...
Bean后处理器在初始化之前对 getContextViaBean 进行增强处理...
Bean后处理器在初始化之后对 getContextViaBean 进行增强处理...
Bean后处理器在初始化之前对 getField 进行增强处理...
Bean后处理器在初始化之后对 getField 进行增强处理...
Spring正在执行默认构造函数构造Chinese实例...
14 依赖注入的值:Spring正在执行setBeanName()方法注入依赖关系...
15 Bean后处理器在初始化之前对 chinese 进行增强处理...
16 正在执行初始化方法afterPropertiesSet...
17 正在执行初始化方法init...
18 Bean后处理器在初始化之后对 chinese 进行增强处理...
19 Bean后处理器将Chinese.beanName修改为'Java编程'.
20 Java编程:Spring正在执行setBeanName()方法注入依赖关系...
我打算去砍点柴火
修改beanName=Java编程,钢斧砍柴好快

从执行结果可以看到,受BeanPostProcessor影响,所有Bean都被插入了两条特定行为,chinese Bean由于额外实现了InitializingBean接口而多了两条额外行为。

另外可以看到虽然在配置文件中chinese的beanName被注入的是“依赖注入的值”,但是初始化之后它又被修改成了“Java编程”,所以setBeanName也被调用了两次。

从上面的例子可以看出,BeanPostProcessor接口的作用就是对容器bean进行批量处理,实际中Spring的这种后处理很有用,通常用来实现代理器,

例如BeanNameAutoProxyCreator是根据Bean实例的name属性,创建Bean实例的代理。

DefaultAdvisorProxyCreator是根据提供的Advisor对容器所有Bean实例创建代理。

2.容器后处理器

类似的,容器也有后处理器,专门用来扩展容器的功能。容器后处理器需要实现BeanFactoryPostProcesser接口。

Spring为我们实现了几个容器后处理器,常用的有PropertyPlaceholderConfigurer, PropertyOverrideConfigurer等。

PropertyPlaceholderConfigurer可以用property文件来替换Spring配置文件中的占位符变量,典型用法如,

     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>dbconn.properties</value>
<value>dbconn.properties2</value>
<value>dbconn.properties3</value>
</list>
</property>
</bean> <bean id="dataSource" class="conn.machange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
p:driverClass="${jdbc.driverClassName}"
p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.username}"
p:password="${jdbc.password}" />

上面定义的dataSource Bean,并没有直接将值注入driverClass, jdbcUrl等属性中,而是使用${jdbc.driverClassName},${jdbc.url},

因为有了容器后处理器PropertyPlaceholderConfigurer的存在,Spring会查找对应的外部properties文件并用里面设置的变量替换Spring中的变量,外部properties文件内容如下,

dbconn.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.pawword=123456

另外还有PropertyOverrideConfigurer容器后处理器,这个跟前面有点类似都是用来将Spring配置提取到外部properties文件中,但是功能更强大。

PropertyOverrideConfigurer属性文件指定的信息可以直接覆盖Spring配置的元数据,还是上面的例子,

     <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations">
<list>
<value>dbconn.properties</value>
<value>dbconn.properties2</value>
<value>dbconn.properties3</value>
</list>
</property>
</bean> <bean id="dataSource" class="conn.machange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" />

可以看到dataSource这个bean没有最注入任何属依赖属性,但是在下面的外部properties文件中,却配置了dataSource Bean的属性,注意这里的属性名称必须是Bean确实拥有的才行。

dbconn.properties
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/test
dataSource.username=root
dataSource.pawword=123456

3.Spring的Annotation

使用Spring的Annotation可以不用再在XML文件中进行bean的配置,只需要在XML中设置好bean的搜索路径,然后在bean上使用相应的Annotation就行了。

XML配置Bean搜索路径如下,使用<context:component-scan的base-package属性进行配置,

还可以使用<context:include-filter和<context:exclude-filter进行Bean路径的配置,这种配置不需要在Bean类名上加注解,容器就能自动识别出bean

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 自动扫描指定包及其子包下的所有Bean类 -->
<context:component-scan base-package="spi" />
<context:include-filter type="regex" expression=".*Chinese" />
<context:exclude-filter type="regex" expression="Stone*" />
</beans>

3.1@Componnet

接着我们只需要将Bean类名加上Annotation注解就行了,Spring可以使用@Component, @Controller, @Service, @Repository四种注解将一个类标识为容器中的bean,最常用的是@Component.

 @Component
public class Chinese implements Person, InitializingBean {
... @Component
public class SteelAxe implements Axe {
... @Component
public class StoneAxe implements Axe {
...

3.2@Scope

@Scope用来指定Bean的作用域,与XML配置中的scope属性意义一样

 @Scope("prototype")
@Component
public class Chinese implements Person, InitializingBean {
...

3.3@Resource

Spring通过@Resource设置依赖关系,用来修饰setter方法,相当于XML配置中的<property>元素,@Resource中有一个name属性,为name属性设置值就相当于设置ref属性。

     @Resource(name="stoneAxe")
public void setAge(int age) {
this.age = age;
}

@Resource也可以直接修饰实例变量,一样会进行依赖注入,并且括号中的name可以省略,

     @Resource(name="stoneAxe")
private Axe axe;
//或者
@Resource(”stoneAxe")
private Axe axe;

3.4@PostConstruct和@PreDestroy

这两个注解相当于XML中的init-method和destroy-method属性,会在bean初始化期间添加额外行为,只不过这两个注解不需要任何属性值,直接将注解放在对应的方法上即可

     @PostConstruct
public void init() {
System.out.println("正在执行初始化方法init...");
}
@PreDestroy
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("正在执行初始化方法afterPropertiesSet...");
}

3.5@DependsOn和@Lazy

@DependsOn用于强制初始化其他(依赖的)多个bean,以数组形式作为此注解的参数值,

 @DependsOn({"steelAxe","stoneAxe"})
@Scope("prototype")
@Component
public class Chinese implements Person, InitializingBean {
...

3.6@Autowired

Spring4.0中的@Autowired进行了功能增强,可以用来修饰setter方法,普通方法,实例变量和构造器,甚至是数组变量。默认使用byType(按参数类型)自动装配策略。

     // 修饰setter方法
@Autowired
public void setAge(int age) {
this.age = age;
}
...
//修饰普通方法,支持多个参数
@Autowired
public void test(Axe axe, Person person) { }
...
//修饰成员变量
@Autowired
private Axe axe;
...
//修饰构造器
@Autowired
public Chinese(Axe axe) {
this.axe = axe;
}
...
//修饰数组,Spring会先收集类型相符的bean,构造数组,然后进行依赖注入
@Autowired
private Axe[] axes;
...

需要注意的是,@Autowired除了修饰数组之外,对于其他情况(setter方法,普通方法,成员变量,构造函数),如果Spring在容器中找到超过一个符合类型的bean,将会报错

@Autowired默认使用byType策略进行依赖注入时bean的查找,如果希望指定依赖注入的bean id,则需要使用@Qualifier

     @Qualifier("steelAxe")
private Axe axe;

当然,这么做就没什么意义了,因为@Resource完全可以实现这个功能,@Qualifier还有个用处就是可以指定方法形参时的依赖注入,

     public void setAxe(@Qualifier("steelAxe") Axe axe) {
this.axe = axe;
}

4.Resource接口

Spring提供了一个Resource接口用来访问资源,其底层是封装了各种各种资源的访问方式,例如网络资源,本地资源,二进制数据等等,

通过Resource实现类的对象,可以对资源进行统一方式的操作,还可以通过Rescource实现类的对象获取资源的File实例,以java IO的方式操作资源。

Resource提供如下对资源的统一访问方法:getInputStream(), exists(), isOpen(), getDescription(), getFile(), getUrl()。

Spring为Resource默认已经实现了如下类:UrlResource, ClassPathResource, FileSystemResource, ServletContextResource, InputStreamResource, ByteArrayResource.

下面是几个实现类的用法,

     public static void test10() throws IOException {
UrlResource ur = new UrlResource("file:book.xml");
//ClassPathResource ur = new ClassPathResource("book.xml");
//FileSystemResource ur = new FileSystemResource("book.xml");
//ServletContextResource ur = new ServletContextResource(application,"WEB-INF/book.xml");
//String file="Java编程核心思想";
//byte[] fileBytes = file.getBytes();
//ByteArrayResource ur = new ByteArrayResource(fileBytes);
System.out.println(ur.getFilename());
System.out.println(ur.getDescription());
File file = ur.getFile();
System.out.println(file.getName());
}

可以看到上面代码中,对于每一种Resource实现类,在Spring使用时仅仅是获取资源的方式不同,但是操作文件和数据一模一样,都是通过Resourcce实现类获取资源对象,以相同方法获取资源信息,都可以转化为File实例进行后续操作,当然对于二进制数据就没有文件的说法,所以getFileName之类的方法是不起作用的。

另外还注意到,不同的Resource实现类在获取资源时,传入的参数类型不尽相同,有的需要http:前缀,有的需要file:前缀,有的不需要前缀,这是由实现类的底层性质针对不同类型的资源决定的。

5.ResourceLoader和ResourceLoaderAware接口

在上面的例子中,我们总是需要显式地写出要使用哪个Resource的实现类来获取资源,这显得比较麻烦,Spring又为我们提供了两个接口,

ResourceLoader 用来获取一个Resource实例

ResourceLoaderAware 获取Resource实例的引用

通过ResourceLoader,我们可以实现面向接口的编程,直接获取一个资源,

由于ApplicationContext类也实现了ResourceLoader接口,所以可以直接把ApplicationContext当作一个ResourceLoader,用ApplicationContext实例去获取一个资源,像这样:

 Resource res =ctx.getResource("beans2.xml");

这样就直接获取了一个资源实例,简化了编程,不过这样获取的资源,其底层是通过什么方式(即由哪个Resource的实现类)来获取资源的呢,

Spring采用的是典型的策略模式,根据ApplicationContext实例化时候的策略,来决定这里获取Resource的策略。应用程序只需要使用统一的接口,调用相同的实现方法,而不需要关系底层到底是用了哪个实现类。

如果ApplicationContext实例化使用的是ClassPathXmlApplicationContext方式,那么Resource就是采用ClassPathResource实现类;

如果ApplicationContext实例化使用的是FileSystemXmlApplicationContext方式,那么Resource就是采用FileSystemResource实现类,

以此类推。

因此前面获取资源的代码我们就可以简化如下:

     public static void test11() throws IOException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans2.xml");
//ApplicationContext ctx = new FileSystemXmlApplicationContext("beans2.xml");
Resource res =ctx.getResource("beans2.xml");
System.out.println(res.getFilename());
System.out.println(res.getDescription());
File file = res.getFile();
System.out.println(file.getName());
}

这样就将获取资源的Resource实现类解耦了,我们只需要直接使用Resource接口即可。

当然,如果你非要指定具体的Resource实现类也是可以的,只需要在ctx.getResource(..)时,在传入的参数前加不同的前缀,例如

         //无前缀表示由ApplicationContext来决定加载策略
Resource res =ctx.getResource("beans2.xml");
//以ClassPathResource访问类加载路径下的资源
Resource res =ctx.getResource("classpath:beans2.xml");
//以UrlResource实例访问本地系统
Resource res =ctx.getResource("file:beans2.xml");
//以UrlResource实例访问基于HTTP的网络资源
Resource res =ctx.getResource("http:beans2.xml");

6.将Resource实例作为Bean属性

比ResourceLoader更能解耦的方式是直接将Resource的实例作为Bean的属性,这样就能直接在XML文件中配置需要访问的资源名称,例如这样,

 public class Chinese implements Person, InitializingBean {
private Resource res;
public Resource getRes() {
return res;
}
public void setRes(Resource res) {
this.res = res;
}
...

这样便可以将具体的资源名称写入配置文件,使得能最大显得地将资源名称与具体java代码解耦

<bean id="chinese" class="spi.Chinese" p:res="classpath:book.xml" />

深入了解Spring的更多相关文章

  1. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  2. 玩转spring boot——快速开始

    开发环境: IED环境:Eclipse JDK版本:1.8 maven版本:3.3.9 一.创建一个spring boot的mcv web应用程序 打开Eclipse,新建Maven项目 选择quic ...

  3. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  4. [Spring]IoC容器之进击的注解

    先啰嗦两句: 第一次在博客园使用markdown编辑,感觉渲染样式差强人意,还是github的样式比较顺眼. 概述 Spring2.5 引入了注解. 于是,一个问题产生了:使用注解方式注入 JavaB ...

  5. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  6. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  7. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  8. 为什么做java的web开发我们会使用struts2,springMVC和spring这样的框架?

    今年我一直在思考web开发里的前后端分离的问题,到了现在也颇有点心得了,随着这个问题的深入,再加以现在公司很多web项目的控制层的技术框架由struts2迁移到springMVC,我突然有了一个新的疑 ...

  9. Spring之旅(2)

    Spring简化Java的下一个理念:基于切面的声明式编程 3.应用切面 依赖注入的目的是让相互协作的组件保持松散耦合:而AOP编程允许你把遍布应用各处的功能分离出来形成可重用的组件. AOP面向切面 ...

  10. Spring之旅

    Java使得以模块化构建复杂应用系统成为可能,它为Applet而来,但为组件化而留. Spring是一个开源的框架,最早由Rod Johnson创建.Spring是为了解决企业级应用开发的复杂性而创建 ...

随机推荐

  1. jdbc 读取oracle long raw 字段,里面存的是文本

    jdbc 读取oracle long raw 字段,里面存的是文本 参考: http://singlewolf.iteye.com/blog/278769 http://blog.csdn.net/r ...

  2. winform设置超时时间

    ); //设置超时时间 var completedTask = await Task.WhenAny(new Task(async () => { );//执行的方法示例这里用延迟代替 }), ...

  3. MVC地区多级联动扩展实现(非递归形式)

    MVC前台界面调用方式如下: @Html.AreaDropDownList(, string.Empty) 参数说明: 第一个参数控件的名称: 第二个参数选中的地区编码: 第三个参数地区层级: 第四个 ...

  4. Errors occurred during the build. Errors running builder &#39;Integrated External Tool Builder&#39; on proje

    Errors occurred during the build. Errors running builder 'Integrated External Tool Builder' on proje ...

  5. Java web測试分为6个部分

    1.功能測试 2.性能測试(包含负载/压力測试)3.用户界面測试 4. 兼容性測试 5.  安全測试  6.接口測试   1 功能測试 1.1 链接測试 链接測试可分为三个方面. 首先,測试全部链接是 ...

  6. ubantu安装jdk

    环境:ubantu16.04下安装jdk1.8 1,在当前用户根目录下创建目录,本人所用的用户为bruce: mkdir /home/bruce/jdk 2,官网下载jdk1.8,网址为http:// ...

  7. cookie,session,viewstate

    viewstate的原理是隐藏域. protected void Page_Load(object sender, EventArgs e) { ViewState["v1"] = ...

  8. ThinkPHP搜索框需要注意的事项

    1.当搜索成功后需要用到分页的时候,form表单需要用get传参 2.编码方式 当编码方式不正确的时候,使用分页类改变分页,会使搜索框里面的内容乱码 改变编码方式的方法 第一种:header(&quo ...

  9. 豆瓣项目(用react+webpack)

    用豆瓣电影api的项目 电影列表组件渲染 步骤: 1. 发送Ajax请求 1.1 在组件的componentWillMount这个生命周期钩子中发送请求 1.2 发送ajax XMLHttpReque ...

  10. 函数的arguments

    1.arguments a.只能在函数内部被访问. b.arguments是一个伪数组(有下标索引,可以存放多个值,但是他里面没有数组的方法.) c.arguments里面存的是什么?函数的实际参数传 ...