spring知识点整理-第一篇

一、@Autowired与@Resource

 相同点:都可以用来装配bean. 都可以写在字段上,或写在setter方法上。       

不同点:1.@Autowired 默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false);如果我们想使用名称装配可以结合@Qualifier注解进行使用 。

2.@Resource 默认安装名称进行装配(这个注解属于J2EE的),名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时,@Resourve注解会回退到按类型装配。但一旦指定了name属性,就只能按照名称装配了。

代码:

@Resource(name="testService")  
private TestService testService;  

二、spring bean的作用域分为以下五种:

1、singleton(默认模式):单例,指一个bean容器中只存在一份。

2、prototype:每次请求(每次使用)创建新的实例,destroy方式不生效。

3、request:每次http请求创建一个实例且仅在当前request内有效。

4、session:同上,每次http请求创建,当前session中有效。

5、global session:基于portlet的web中有效(portlet定义了global sessio),如果在web中,同session。

代码:

a.xml的配置

<!-- 设置bean的作用域scope属性值为prototype,默认为singleton,可以不设置scope属性 -->
<bean name="beanScope" class="com.jsun.test.springDemo.BeanScope" scope="prototype"></bean>

b.annotation注解方式

//注册bean
@Component("beanScope") 
//设置bean的作用域范围为singleton
@Scope("singleton")  
public class BeanScope {

}

三、spring bean的生命周期

1、xml文件中配置bean的init-method属性和destroy-method属性

<bean name="beanScope" class="com.jsun.test.springDemo.BeanScope" 
init-method="init_m" destroy-method="destroy_m"></bean>

同时需要注意的是:如果bean中配置init-method和init-destroy属性,那么在bean类中必须提供对应的初始化和销毁方法,否则将抛出初始化异常:Exception encountered during context initialization 。如果配置全局的,在<beans></beans>上进行配置。

2、实现

org.springframework.beans.factory.DisposableBean/org.springframework.beans.factory.InitializingBean接口,覆盖实现afterPropertiesSet()和destroy()方法.

public class BeanScope implements InitializingBean,DisposableBean {

    public BeanScope(){
        System.out.println("BeanScope 实例化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("实现接口InitializingBean afterPropertiesSet()初始化");

    }

    @Override
    public void destroy() throws Exception {
        System.out.println("实现接口DisposableBean destroy()销毁");

    }
}

注:如果配置了bean的init-method和init-destroy属性(或同时配置全局的default-init-method和default-destroy-method属性),并实现了上述的初始化和销毁接口,接口的初始化和销毁方法将先于配置的初始化和销毁方法。

3、注解配置方式

在指定方法上加上@PostConstruct或@PreDestroy注解来指定该方法作为初始化或销毁方法来使用。

public class BeanScope{

    public BeanScope(){
        System.out.println("BeanScope bean 实例化");
    }

    //添加@PostConstruct注解,指定当前方法作为初始化方法
    @PostConstruct
    public void init(){
        System.out.println("@PostConstruct注解 初始化");
    }

    //添加@PreDestroy注解,指定当前方法作为销毁方法
    @PreDestroy
    public void destroy(){
        System.out.println("@PreDestroy注解 销毁");
    }
}

四、bean的作用域与生命周期之间的关系以及初始化时机

1、初始化bean实例是时机与bean作用域的关系

(1)、当scope=singleton,即默认情况,会在容器初始化时实例化、初始化。但如果指定bean节点的lazy-init=”true”,来延迟bean的实例化、初始化,当第一次获取bean时才会初始化bean。

(2)、当scope=prototype时,也会延迟初始化bean,相当于设置lazy-init=”true”,即第一次请求该bean时才初始化(如调用getBean()方法时);并且当spring容器关闭时,destroy()销毁方法也不会被调用。

原因:对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。

五、InitializingBean的作用

InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。

import org.springframework.beans.factory.InitializingBean;
public class TestInitializingBean implements InitializingBean{
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("ceshi InitializingBean");        
    }
    public void testInit(){
        System.out.println("ceshi init-method");        
    }
}

在spring初始化bean的时候,如果该bean是实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertiesSet方法,然后在调用init-method中指定的方法。

总结

1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用。

2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖。

3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

六、ApplicationContextAware、ApplicationListener的作用和关于spring中Aware结尾接口介绍

1、首先来介绍以Aware为结尾接口

Spring中提供一些Aware结尾相关接口,像是BeanFactoryAware、 BeanNameAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等等。实现这些 Aware接口的Bean在被实例化 之后,可以取得一些相对应的资源,例如实现BeanFactoryAware的Bean在实例化后,Spring容器将会注入BeanFactory的实例,而实现ApplicationContextAware的Bean,在Bean被实例化后,将会被注入 ApplicationContext的实例等等。通过重写setter方法,当前bean被实例化后实现相关实例的注入。

//实现BeanNameAware接口,并重写setBeanName()方法,让Bean获取自己在BeanFactory配置中的名字
//(根据情况是id或者name)
//实现ApplicationContextAware接口,并重写setApplicationContext()方法
public class MyApplicationContext implements BeanNameAware,ApplicationContextAware{
    private String beanName;

    //注入的beanName即为MyApplicationContext在BeanFactory配置中的名字(根据情况是id或者name)
    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
        System.out.println("MyApplicationContext beanName:"+beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        //通过重写的接口方法,获取spring容器实例context,进而获取容器中相关bean资源
        System.out.println(context.getBean(this.beanName).hashCode());

    }

}

注意:除了通过实现Aware结尾接口获取spring内置对象,也可以通过@Autowired注解直接注入相关对象,(如果需要用到静态方法中,如工具方法,还是采用实现接口的方式).

2、ApplicationContextAware、ApplicationListener的作用

什么是ApplicationContext? 
它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些。 
ApplicationContext则是应用的容器。Spring把Bean(object)放在容器中,需要用就通过get方法取出来。

ApplicationEvent

是个抽象类,里面只有一个构造函数和一个长整型的timestamp。
ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法。所以自己的类在实现该接口的时候,要实装该方法。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,这个bean得到通知。其实这就是标准的Oberver设计模式。

其中ApplicationContextAware接口可以实现我们在初始化bean的时候给bean注入ApplicationConxt(Spring上下文对象)对象。ApplicationContextAware接口提供了publishEvent方法,实现了Observe(观察者)设计模式的传播机制,实现了对bean的传播。通过ApplicationContextAware我们可以把系统中所有ApplicationEvent传播给系统中所有的ApplicationListener。因此,我们只需要构造好我们自己的ApplicationEvent和ApplicationListener,就可以在系统中实现相应的监听器。

资料:http://blog.csdn.net/u011955252/article/details/52900690

七、BeanDefinition、PropertyEditorSupport的作用

1.BeanDefinition

将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建。BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。BeanDefinition继承了AttributeAccessor,说明它具有处理属性的能力;BeanDefinition继承了BeanMetadataElement,说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object。

详细解读的流程资料:http://blog.51cto.com/dba10g/1726519

2、PropertyEditorSupport

通过继承JDK 中的 java.beans.PropertyEditorSupport 类来实现自己的编辑器类 ,该类用于实现将String 类型转换成您需要的数据类型。然后我们只需要在Spring 的容器中对这个编辑器进行有效的“注册”便可以实现Spring 在装配Bean 时自动的将String 类型转换成我们自定义的类型。

如何编辑自己的PropertyEditor,其实需要了解一点java.beans包的知识,在该包中,有一个 java.beans.PropertyEditor的接口,它定义了一套接口方法(12个),即通过这些方法如何将一个String变成内部的一个对 象,这两个方法是比较重要的: 
     a)setValue(Object value) 直接设置一个对象
     b)setAsText(String text) 通过一个字符串来构造对象,一般在此方法中解析字符串,将构造一个类对象,调用setValue(Object)来完成属性对象设置操作。 
  2)实现所有的接口方法是麻烦的,java.beans.PropertyEditorSupport 适时登场,一般情况下,我们通过扩展这个方便类即可。比如转换时间格式:

import java.beans.PropertyEditorSupport;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;

import org.jflame.toolkit.util.DateHelper;
import org.springframework.util.StringUtils;

/**
 * 多日期格式转换器<p>
 * 默认支持格式:"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss","yyyy年MM月dd日","yyyyMMddHHmmss"
 * 
 *
 */
public class MyCustomDateEditor extends PropertyEditorSupport {
	
	private final DateFormat defaultDateFormat;
	
	public MyCustomDateEditor(){
		defaultDateFormat=null;
	}
	
	/**
	 * 构造函数,指定特定格式DateFormat
	 * @param dateFormat
	 */
	public MyCustomDateEditor(DateFormat dateFormat) {
		this.defaultDateFormat = dateFormat;
	}
	
	/**
	 * Parse the Date from the given text, using the specified DateFormat.
	 */
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		if (!StringUtils.hasText(text)) {
			setValue(null);
		}
		else {
			try {
				//未指定固定格式器,使用所有支持格式处理
				if(defaultDateFormat==null){
					setValue(DateHelper.parseDate(text, DateHelper.formats));
				}else{
					setValue(this.defaultDateFormat.parse(text));
				}
			}
			catch (ParseException ex) {
				throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
			}
		}
	}

	/**
	 * Format the Date as String, using the specified DateFormat.
	 */
	@Override
	public String getAsText() {
		Date value = (Date) getValue();
		String dateStr = "";
		if (value != null) {
			if (defaultDateFormat == null) {
				if (value instanceof java.sql.Date) {
					dateStr = DateHelper.format(value, DateHelper.CN_YYYY_MM_DD);
				} else {
					dateStr = DateHelper.format(value, DateHelper.YYYY_MM_DD_HH_mm_ss);
				}
			} else {
				dateStr = this.defaultDateFormat.format(value);
			}
		}
		return dateStr;
	}
}

八、@Configuration注解、@Bean注解的使用

   1、@Configuration注解、@Bean注解

 Spring中为了减少xml中配置,可以声明一个配置类(例如SpringConfig)来对bean进行配置。

我们需要在用于指定配置信息的类上加上 @Configuration 注解,以明确指出该类是 Bean 配置的信息源。并且 Spring 对标注 Configuration 的类有如下要求: 

      (a) 配置类不能是 final 的;

      (b)配置类不能是本地化的,亦即不能将配置类定义在其他类的方法内部;

      (c)配置类必须有一个无参构造函数。

@Configuration //在类上配置注解
public class BookStoreDaoConfig{  
@Bean   //在方法上配置注解
public UserDao userDao(){
   return new UserDaoImpl();//配置类必须有一个无参构造函数
}  
@Bean  
public BookDao bookDao(){
   return new BookDaoImpl();
}  
}  

注意:Spring 在解析到以上文件时,将识别出标注 @Bean 的所有方法,执行之,并将方法的返回值 ( 这里是 UserDaoImpl 和 BookDaoImpl 对象 ) 注册到 IoC 容器中。默认情况下,Bean 的名字即为方法名。

a.@Bean 具有以下四个属性:  

name -- 指定一个或者多个 Bean 的名字。这等价于 XML 配置中 的 name 属性。

initMethod -- 容器在初始化完 Bean 之后,会调用该属性指定的方法。这等价于 XML 配置中 的 init-method 属性。

destroyMethod -- 该属性与 initMethod 功能相似,在容器销毁 Bean 之前,会调用该属性指定的方法。这等价于 XML 配置中 的 destroy-method 属性。

autowire -- 指定 Bean 属性的自动装配策略,取值是 Autowire 类型的三个静态属性。

      1. Autowire.BY_NAME,

       2.Autowire.BY_TYPE,

       3.Autowire.NO。

与 XML 配置中的 autowire 属性的取值相比,这里少了 constructor,这是因为 constructor 在这里已经没有意义了。@Bean 没有直接提供指定作用域的属性,可以通过 @Scope 来实现该功能。

b.@Configuration注解

@Target({ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface Configuration {  
String value() default "";  

九、@Value和<util:properties>的使用

资料:http://blog.csdn.net/wangshfa/article/details/26674839

十、spring中的父子容器

spring总的上下文容器有父子之分,父容器和子容器。 父容器对子容器可见,子容器对父容器不可见 。

父子容器的主要用途是上下文隔离。以spring和SpringMVC为例子。需要先熟悉spring是怎样在web容器中启动起来的。spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

何为父子上下文呢?

父上下文:

使用listener监听器来加载配置文件,如下:

<listener>   

  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   

</listener>


Spring 会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

子上下文:

使用Spring MVC 来处理拦截相关的请求时,会配置DispatchServlet:

<servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet

    </servlet-class>

    <init-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

    <servlet-name>dispatcherServlet</servlet-name>

    <url-pattern>/</url-pattern>

</servlet-mapping>


每个DispatchServlet会有一个自己的上下文,称为子上下文,它也保存在 ServletContext中,key 是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一 个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + ".CONTEXT"。

可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);

父上下文(父容器)和子上下文(子容器)的访问权限:

子上下文可以访问父上下文中的bean,但是父上下文不可以访问子上下文中的bean。

父上下文使用与否

方案一,传统型:

父上下文容器中保存数据源、服务层、DAO层、事务的Bean。

子上下文容器中保存Mvc相关的Action的Bean.

事务控制在服务层。

由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。

当然,做为“传统型”方案,也没有必要这要做。

方案二,激进型:

Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了-----没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。

由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServl

十一、@DependsOn注解的作用

转载于:https://my.oschina.net/u/2380961/blog/1590071

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值