Spring Framework

 

目录

介绍:

Spring IoC 容器

依赖注入

Spring Beans 自动装配

Spring 基于注解的配置

Spring 中的事件处理

Spring 框架的 AOP

Spring 事务管理


介绍:

Spring Framework核心支持依赖注入,事务管理,Web应用程序,数据访问,消息传递,测试和更多功能。

Spring框架为现代基于Java的企业应用程序提供了一个全面的编程和配置模型 - 在任何类型的部署平台上。

Spring的一个关键元素是应用程序级别的基础架构支持:Spring着重于企业应用程序的“管道”,以便团队可以专注于应用程序级业务逻辑,而不必与特定部署环境形成不必要的联系。

Spring 的优良特性:

  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
  • 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自己在代码中new创建。而使用 Spring 之后。对象的创建都是由给了 Spring 框架。
  • 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
  • 面向切面编程:Aspect Oriented Programming——AOP
  • 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表述层的 SpringMVC 和持久层的 Spring JDBC)

版本要求:

  • JDK 8+ for Spring Framework 5.x
  • JDK 6+ for Spring Framework 4.x
  • JDK 5+ for Spring Framework 3.x

三层架构 :

  • A 表现层   web层    MVC是表现层的一个设计模型 
  • B 业务层  service层
  • C 持久层  dao层

核心容器:

核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:

  • spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。

  • spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。

  • context模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。

  • spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。

它们的完整依赖关系如下图所示:

数据访问/集成

数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:

(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)

  • JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。

  • ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。

  • OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。

  • JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。

  • 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)

Web:

Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:

  • Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。

  • Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。

  • Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。

  • Web-Portlet 模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。

其他模块:

还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:

  • AOP 模块提供了面向切面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。

  • Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

  • Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

  • Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

  • 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。

Spring IoC 容器

IoC 容器:Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans.通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示.通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。

Spring 提供了以下两种不同类型的容器:

Spring BeanFactory 容器:它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 和BeanFactoryAware等相关接口仍然在Spring中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。

Spring ApplicationContext 容器:Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean,还可以从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。

最常被使用的 ApplicationContext 接口实现:

  • FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。

  • ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。

  • WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。

ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常不建议使用BeanFactory。

public class MainApp {
    public static void main(String[] args) {
        //第一步是我们使用框架 API ClassPathXmlApplicationContext() 来创建应用程序的上下文。
        //这个 API 加载 beans 的配置文件并最终基于所提供的 API,它处理创建并初始化所有的对象,即在配置文件中提到的 beans
        ApplicationContext context =
                new ClassPathXmlApplicationContext("Beans.xml");
        //第二步是使用已创建的上下文的 getBean() 方法来获得所需的 bean。这个方法使用 bean 的 ID
        //返回一个最终可以转换为实际对象的通用对象。一旦有了对象,你就可以使用这个对象调用任何类的方法。
        HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
        obj.getMessage();
        XmlBeanFactory factory = new XmlBeanFactory
                (new ClassPathResource("Beans.xml"));
        HelloWorld obj2 = (HelloWorld) factory.getBean("helloWorld");
        obj2.getMessage();
        ApplicationContext context2 = new FileSystemXmlApplicationContext
                ("E:/ideaProject/src/main/resources/Beans.xml");
        HelloWorld obj3 = (HelloWorld) context.getBean("helloWorld");
        obj.getMessage();
    }
}

Beans.xml定义如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="helloWorld" class="com.li.HelloWorld">
        <property name="message" value="Hello World!"/>
    </bean>

</beans>

Spring Bean 定义

被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,例如上面在 XML 的表单中的定义。

元数据各个属性的定义:

Bean 作用域  -> scope

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
    <!-- collaborators and configuration for this bean go here -->
</bean>

<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="prototype">
   <!-- collaborators and configuration for this bean go here -->
</bean>

 ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
 HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
 objA.setMessage("I'm object A");
 objA.getMessage();
 HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
 objB.getMessage();

Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象,根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

Bean 生命周期

为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。Bean的生命周期可以表达为:Bean的定义——Bean的初始化——Bean的使用——Bean的销毁。

初始化回调:可以继承一个借口 ,org.springframework.beans.factory.InitializingBean 接口指定一个单一的方法,也可以基于 XML 的配置元数据的情况下,你可以使用 init-method 属性来指定带有 void 无参数方法的名称。

public class ExampleBean implements InitializingBean {
   public void afterPropertiesSet() {
      // do some initialization work
   }
}
<bean id="exampleBean" 
         class="examples.ExampleBean" init-method="init"/>

public class ExampleBean {
   public void init() {
      // do some initialization work
   }
}

销毁回调:org.springframework.beans.factory.DisposableBean 接口指定一个单一的方法或 XML 的配置元数据的destroy-method 属性来指定带有 void 无参数方法的名称。

public class ExampleBean implements DisposableBean {
   public void destroy() {
      // do some destruction work
   }
}

<bean id="exampleBean"
         class="examples.ExampleBean" destroy-method="destroy"/>

public class ExampleBean {
   public void destroy() {
      // do some destruction work
   }
}

如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法销毁方法。框架使用 元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示:

整体:
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="helloWorld" 
       class="com.tutorialspoint.HelloWorld"
       init-method="init" destroy-method="destroy">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

多个:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-init-method="init" 
    default-destroy-method="destroy">

   <bean id="..." class="...">
       <!-- collaborators and configuration for this bean go here -->
   </bean>

</beans>

Bean 后置处理器

Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。

BeanPostProcessor 接口定义回调方法,实现该接口来提供自己的实例化逻辑,依赖解析逻辑等。也可以在 Spring 容器通过插入一个或多个 BeanPostProcessor 的实现 在Spring IoC 容器实例化一个 bean 实例后 BeanPostProcessor 接口进行它们的工作。

你可以配置多个 BeanPostProcessor 接口,通过设置 BeanPostProcessor 实现的 Ordered 接口提供的 order 属性来控制这些 BeanPostProcessor 接口的执行顺序。

ApplicationContext 会自动检测由 BeanPostProcessor 接口的实现定义的 bean,注册这些 bean 为后置处理器,然后通过在容器中创建 bean,在适当的时候调用它。BeanPostProcessor 是针对所有的bean。

需要注册一个在 AbstractApplicationContext 类中声明的关闭 hook 的 registerShutdownHook() 方法。它将确保正常关闭,并且调用相关的 destroy 方法。


    <bean id="helloWorld" class="com.li.HelloWorld"
          init-method="init" destroy-method="destroy">
        <property name="message" value="Hello World!"/>
    </bean>

    <bean id="helloWorld2" class="com.li.HelloWorld2">
        <property name="message" value="Hello World2!"/>
    </bean>

    <bean class="com.li.InitHelloWorld" />

    AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
    HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
    HelloWorld2 obj2 = (HelloWorld2) context.getBean("helloWorld2");
    obj.getMessage();
    obj2.getMessage();
    context.registerShutdownHook();

    BeforeInitialization : helloWorld
    Bean is going through init.
    AfterInitialization : helloWorld
    BeforeInitialization : helloWorld2
    AfterInitialization : helloWorld2
    Your Message : Hello World!
    Your Message : Hello World2!
    Bean will destroy now.
    

public class InitHelloWorld implements BeanPostProcessor {
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      System.out.println("BeforeInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      System.out.println("AfterInitialization : " + beanName);
      return bean;  // you can return any other object as well
   }
}

 Bean 定义继承

使用 parent 属性可以实现子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值。Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。你可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置。

 <bean id="helloWorld" class="com.li.HelloWorld"
          init-method="init" destroy-method="destroy">
        <property name="message" value="Hello World!"/>
    </bean>

    <bean id="helloWorld2" class="com.li.HelloWorld2" parent="helloWorld">
        <!-- 此时HelloWorld2 也要有init 和destory方法-->
    </bean>

    <bean class="com.li.InitHelloWorld" />

    AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
    HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
    HelloWorld2 obj2 = (HelloWorld2) context.getBean("helloWorld2");
    obj.getMessage();
    obj2.getMessage();
    context.registerShutdownHook();

    BeforeInitialization : helloWorld
    Bean is going through init.
    AfterInitialization : helloWorld
    BeforeInitialization : helloWorld2
    Bean is going through init.
    AfterInitialization : helloWorld2
    Your Message : Hello World!
    Your Message : Hello World!
    Bean will destroy now.
    Bean will destroy now.

Bean 定义模板

你可以创建一个 Bean 定义模板,它可以被其他子 bean 定义使用。在定义一个 Bean 定义模板时,你不应该指定的属性,而应该指定带 true 值的抽象属性,如下所示:父 bean 自身不能被实例化,因为它是不完整的,而且它也被明确地标记为抽象的。当一个定义是抽象的,它仅仅作为一个纯粹的模板 bean 定义来使用的,充当子定义的父定义使用。

    <bean id="beanTeamplate" abstract="true">
        <property name="message" value="Hello World!"/>
        <property name="message1" value="Hello World!"/>
    </bean>

    <bean id="helloIndia" class="com.li.HelloWorld" parent="beanTeamplate">
        <property name="message" value="Hello India!"/>
    </bean>

    AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
    HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
    obj.getMessage();
    context.registerShutdownHook();

    //Your Message : Hello India!

依赖注入

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。

Spring 基于构造函数的依赖注入

当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。

 <!-- Definition for textEditor bean -->
    <bean id="textEditor" class="com.li.TextEditor">
        <constructor-arg ref="spellChecker"/>
        <constructor-arg type="int" value="2001"/>
        <constructor-arg index="2" value="Zara"/>
        <constructor-arg index="3" ref="spellChecker"/>
    </bean>

    <!-- Definition for spellChecker bean -->
    <bean id="spellChecker" class="com.li.SpellChecker">
    </bean>

public class TextEditor {
    private SpellChecker spellChecker;
    private int index;
    private String text;
    private SpellChecker spellChecker2;
    public TextEditor(SpellChecker spellChecker, int index, String text, SpellChecker spellChecker2) {
        System.out.println("Inside TextEditor constructor." +  index + " -- " + text);
        this.spellChecker = spellChecker;
        this.index = index;
        this.text = text;
        spellChecker2.checkSpelling();
    }
    public void spellCheck() {
        spellChecker.checkSpelling();
    }
}


public class SpellChecker {
    public SpellChecker(){
        System.out.println("Inside SpellChecker constructor." );
    }
    public void checkSpelling() {
        System.out.println("Inside checkSpelling." );
    }
}

ApplicationContext context =
   new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();

Inside SpellChecker constructor.
Inside TextEditor constructor.2001 -- Zara
Inside checkSpelling.
Inside checkSpelling.

注释:上面这个例子里,将依赖类 SpellChecker.java注入到TextEditor.java 文件。如此,便称为依赖注入。如果存在不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,那么构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序就可以了。也可以使用 type 属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型。最后并且也是最好的传递构造函数参数的方式,使用 index 属性来显式的指定构造函数参数的索引。如果想要向一个对象传递一个引用,需要使用 标签的 ref 属性代替 value 属性。

Spring 基于设值函数的依赖注入

当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后,通过容器在你的 bean 上调用设值函数,基于设值函数的 DI 就完成了。

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <property name="spellChecker" ref="spellChecker"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。

第二个你需要注意的点是,如果要把一个引用传递给一个对象,那么需要使用 标签的 ref 属性,而如果要直接传递一个值,那么应该使用 value 属性。

使用 p-namespace 实现 XML 配置

如果你有许多的设值函数方法,那么在 XML 配置文件中使用 p-namespace 是非常方便的。

 <bean id="john-classic" class="com.example.Person">
      <property name="name" value="John Doe"/>
      <property name="spouse" ref="jane"/>
   </bean>

   <bean name="jane" class="com.example.Person">
      <property name="name" value="John Doe"/>
   </bean>

上述 XML 配置文件可以使用 p-namespace 以一种更简洁的方式重写


<bean id="john-classic" class="com.example.Person"
      p:name="John Doe"
      p:spouse-ref="jane"/>
   </bean>

   <bean name="jane" class="com.example.Person"
      p:name="John Doe"/>
   </bean>

Spring 注入内部 Beans

正如 Java 内部类是在其他类的范围内被定义的,同理,inner beans 是在其他 bean 的范围内定义的 bean。因此在其他元素内 元素被称为内部bean。

   <bean id="outerBean" class="...">
      <property name="target">
         <bean id="innerBean" class="..."/>
      </property>
   </bean>

Spring 注入集合

如果想传递多个值,如 Java Collection 类型 List、Set、Map 和 Properties,应该怎么做呢。为了处理这种情况,Spring 提供了四种类型的集合的配置元素,

你可以使用<list><set>来连接任何 java.util.Collection 的实现或数组。


    <bean id="javaCollection" class="com.li.JavaCollection">

        <!-- results in a setAddressList(java.util.List) call -->
        <property name="addressList">
            <list>
                <value>INDIA</value> <!-- 引用<ref bean="address1"/> -->
                <value>Pakistan</value>
                <value>USA</value>
                <value>USA</value>
            </list>
        </property>

        <!-- results in a setAddressSet(java.util.Set) call -->
        <property name="addressSet">
            <set>
                <value>INDIA</value>
                <value>Pakistan</value>
                <value>USA</value>
                <value>USA</value>
            </set>
        </property>

        <!-- results in a setAddressMap(java.util.Map) call -->
        <property name="addressMap">
            <map>
                <entry key="1" value="INDIA"/>  <!-- <entry key ="two" value-ref="address1"/>-->
                <entry key="2" value="Pakistan"/>
                <entry key="3" value="USA"/>
                <entry key="4" value="USA"/>
            </map>
        </property>

        <!-- results in a setAddressProp(java.util.Properties) call -->
        <property name="addressProp">
            <props>
                <prop key="one">INDIA</prop>
                <prop key="two">Pakistan</prop>
                <prop key="three">USA</prop>
                <prop key="four">USA</prop>
            </props>
        </property>

    </bean>


public class JavaCollection {
    List addressList;
    Set  addressSet;
    Map  addressMap;
    Properties addressProp;
    // a setter method to set List
    public void setAddressList(List addressList) {
        this.addressList = addressList;
    }
    // prints and returns all the elements of the list.
    public List getAddressList() {
        System.out.println("List Elements :"  + addressList);
        return addressList;
    }
    // a setter method to set Set
    public void setAddressSet(Set addressSet) {
        this.addressSet = addressSet;
    }
    // prints and returns all the elements of the Set.
    public Set getAddressSet() {
        System.out.println("Set Elements :"  + addressSet);
        return addressSet;
    }
    // a setter method to set Map
    public void setAddressMap(Map addressMap) {
        this.addressMap = addressMap;
    }
    // prints and returns all the elements of the Map.
    public Map getAddressMap() {
        System.out.println("Map Elements :"  + addressMap);
        return addressMap;
    }
    // a setter method to set Property
    public void setAddressProp(Properties addressProp) {
        this.addressProp = addressProp;
    }
    // prints and returns all the elements of the Property.
    public Properties getAddressProp() {
        System.out.println("Property Elements :"  + addressProp);
        return addressProp;
    }
}

        ApplicationContext context =
                new ClassPathXmlApplicationContext("Beans.xml");
        JavaCollection jc=(JavaCollection)context.getBean("javaCollection");
        jc.getAddressList();
        jc.getAddressSet();
        jc.getAddressMap();
        jc.getAddressProp();

List Elements :[INDIA, Pakistan, USA, USA]
Set Elements :[INDIA, Pakistan, USA]
Map Elements :{1=INDIA, 2=Pakistan, 3=USA, 4=USA}
Property Elements :{two=Pakistan, one=INDIA, three=USA, four=USA}

注入 null 和空字符串的值

<bean id="..." class="exampleBean">
   <property name="email" value=""/>
</bean>

<bean id="..." class="exampleBean">
   <property name="email"><null/></property>
</bean>

Spring Beans 自动装配

Spring 容器可以在不使用<constructor-arg><property> 元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。

自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入,你可以使用<bean>元素的 autowire 属性为一个 bean 定义指定自动装配模式。

自动装配的局限性

Spring 自动装配 byName

这种模式由属性名称指定自动装配,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。

 <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
       <property name="spellChecker" ref="spellChecker" />
       <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

使用后

<bean id="textEditor" class="com.tutorialspoint.TextEditor" 
      autowire="byName">
      <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

public class TextEditor {
   private SpellChecker spellChecker;
   private String name;
   public void setSpellChecker( SpellChecker spellChecker ){
      this.spellChecker = spellChecker;
   }
   public SpellChecker getSpellChecker() {
      return spellChecker;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

Spring 自动装配 byType

这种模式由属性类型指定自动装配。在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。

<!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor" 
      autowire="byType">
      <property name="name" value="Generic Text Editor" />
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="SpellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

byName 依赖于目标的Bean id, byType时目标的bean id可以随便起。

Spring 由构造函数自动装配

这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。

<bean id="textEditor" class="com.li.TextEditor"
          autowire="constructor"> <!-- TextEditor 需要有对应的带参数的构造函数-->
    </bean>

    <!-- Definition for spellChecker bean -->
    <bean id="SpellChecker" class="com.li.SpellChecker">
    </bean>

Spring 基于注解的配置

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。

@Required 注释

@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。下面显示的是一个使用 @Required 注释的示例。

 <bean id="student" class="com.li.Student">
        <property name="name"  value="Zara" />

        <!-- try without passing age and check the result -->
        <property name="age"  value="11" />
    </bean>

public class Student {
    private Integer age;
    private String name;
    @Required
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAge() {
        return age;
    }
    @Required
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

     ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println("Name : " + student.getName() );
        System.out.println("Age : " + student.getAge() );

Name : Zara
Age : 11

@Autowired 注释    @Autowired 注释对在哪里和如何完成自动连接提供了更多的细微的控制。

Setter 方法中的 @Autowired,当 Spring遇到一个在 setter 方法中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。这里需要注意:要有 <context:annotation-config/> <!-- 开启注入注解扫描-->

    <context:annotation-config/> <!-- 开启注入注解扫描-->

    <!-- Definition for spellChecker bean -->
    <bean id="spellChecker" class="com.li.SpellChecker">
    </bean>

    <!-- Definition for textEditor bean without constructor-arg  -->
    <bean id="textEditor" class="com.li.TextEditor">
    </bean>
    

Setter 方法中的 @Autowired
   @Autowired
    public void setSpellChecker(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
    }

    public void spellCheck() {
        spellChecker.checkSpelling();
    }

属性中的 @Autowired
 @Autowired
   private SpellChecker spellChecker;
   public TextEditor() {
      System.out.println("Inside TextEditor constructor." );
   }  
   public SpellChecker getSpellChecker( ){
      return spellChecker;
   }  
   public void spellCheck(){
      spellChecker.checkSpelling();
   }

构造函数中的 @Autowired
@Autowired
   public TextEditor(SpellChecker spellChecker){
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck(){
      spellChecker.checkSpelling();
   }

@Autowired 的(required=false)选项

默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。

@Qualifier 注释

可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。

   <!-- Definition for student1 bean -->
   <bean id="student1" class="com.tutorialspoint.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11"/>
   </bean>

   <!-- Definition for student2 bean -->
   <bean id="student2" class="com.tutorialspoint.Student">
      <property name="name"  value="Nuha" />
      <property name="age"  value="2"/>
   </bean>


   @Autowired
   @Qualifier("student1")
   private Student student;

 @PostConstruct, @PreDestroy 

为了定义一个 bean 的安装和卸载,我们使用 init-method 和/或 destroy-method 参数简单的声明一下 。init-method 属性指定了一个方法,该方法在 bean 的实例化阶段会立即被调用。同样地,destroy-method 指定了一个方法,该方法只在一个 bean 从容器中删除之前被调用。

你可以使用 @PostConstruct 注释作为初始化回调函数的一个替代,@PreDestroy 注释作为销毁回调函数的一个替代,

   @PostConstruct
   public void init(){
      System.out.println("Bean is going through init.");
   }
   @PreDestroy
   public void destroy(){
      System.out.println("Bean will destroy now.");
   }

@Resource 注释

可以在字段中或者 setter 方法中使用 @Resource 注释,。@Resource 注释使用一个 ‘name’ 属性,该属性以一个 bean 名称的形式被注入。你可以说,它遵循 by-name 自动连接语义

   @Resource(name= "spellChecker")
   public void setSpellChecker( SpellChecker spellChecker ){
      this.spellChecker = spellChecker;
   }

@Configuration 和 @Bean 注解

带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。@Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。最简单可行的 @Configuration 类如下所示:

@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

上面的代码将等同于下面的 XML 配置:
<beans>
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>

public static void main(String[] args) {
   ApplicationContext ctx = 
   new AnnotationConfigApplicationContext(HelloWorldConfig.class); 
   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}

可以加载各种配置类
public static void main(String[] args) {
   AnnotationConfigApplicationContext ctx = 
   new AnnotationConfigApplicationContext();
   ctx.register(AppConfig.class, OtherConfig.class);
   ctx.register(AdditionalConfig.class);
   ctx.refresh();
   MyService myService = ctx.getBean(MyService.class);
   myService.doStuff();
}

在这里,带有 @Bean 注解的方法名称作为 bean 的 ID,它创建并返回实际的 bean。你的配置类可以声明多个 @Bean。一旦定义了配置类,你就可以使用 AnnotationConfigApplicationContext 来加载并把他们提供给 Spring 容器。

@Import 注解

@import 注解允许从另一个配置类中加载 @Bean 定义。

@Configuration
public class ConfigA {
   @Bean
   public A a() {
      return new A(); 
   }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {
   @Bean
   public B b() {
      return new B(); 
   }
}

ApplicationContext ctx = 
new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);

@Bean 注解支持指定任意的初始化和销毁的回调方法

@Configuration
public class AppConfig {
   @Bean(initMethod = "init", destroyMethod = "cleanup" )
   public Foo foo() {
      return new Foo();
   }
}

指定 Bean 的范围:默认范围是单实例,但是你可以重写带有 @Scope 注解的该方法

@Configuration
public class AppConfig {
   @Bean
   @Scope("prototype")
   public Foo foo() {
      return new Foo();
   }
}

bean 的id 和 name的区别

id和name都是spring 容器中中bean 的唯一标识符。

1. id: 一个bean的唯一标识 , 命名格式必须符合XML ID属性的命名规范

2. name: 可以用特殊字符,并且一个bean可以用多个名称:name=“bean1,bean2,bean3”,用逗号或者分号或者空格隔开。如果没有id,则name的第一个名称默认是id.

spring 容器如何处理同名bean?

1、在spring同一个配置文件中,不能存在id相同的两个bean,否则会报错。
2、在两个不同的spring配置文件中,可以存在id相同的两个bean,启动时,不会报错。这是因为spring
ioc容器在加载bean的过程中,类DefaultListableBeanFactory会对id相同的bean进行处理:后加载的配置文件的bean,覆盖先加载的配置文件的bean。DefaultListableBeanFactory类中,有个属性allowBeanDefinitionOverriding,默认值为true,该值就是用来指定出现两个bean的id相同的情况下,如何进行处理。如果该值为false,则不会进行覆盖,而是抛出异常。

spring 容器如何处理没有指定id、name属性的bean?

如果 一个 bean 标签未指定 id、name 属性,则 spring容器会给其一个默认的id,值为其类全名。

如果有多个bean标签未指定 id、name 属性,则spring容器会按照其出现的次序,分别给其指定 id 值为 “类全名#1”, “类全名#2”

<bean class="com.xxx.UserInfo">  
    <property name="accountName" value="no-id-no-name0"></property>  
</bean>  
  
<bean class="com.xxx.UserInfo">  
    <property name="accountName" value="no-id-no-name1"></property>  
</bean>  
  
<bean class="com.xxx.UserInfo">  
    <property name="accountName" value="no-id-no-name2"></property>  
</bean>  

获取bean的方式:

UserInfo u4 = (UserInfo)ctx.getBean("com.xxx.UserInfo");  
UserInfo u5 = (UserInfo)ctx.getBean("com.xxx.UserInfo#1");  
UserInfo u6 = (UserInfo)ctx.getBean("com.xxx.UserInfo#2"); 

Spring 中的事件处理

Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期,当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。

通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。

ApplicationContext启动后会调用refresh方法,然后在finishRefresh()的时候发送ContextRefreshedEvent事件。这时自定义一个listener类实现ApplicationListener<ApplicationEvent>,那么会自动调用实现类的onApplicationEvent方法

监听上下文事件

为了监听上下文事件,一个 bean 应该实现只有一个方法 onApplicationEvent() 的 ApplicationListener 接口.

public class CStartEventHandler 
   implements ApplicationListener<ContextStartedEvent>{
   public void onApplicationEvent(ContextStartedEvent event) {
      System.out.println("ContextStartedEvent Received");
   }
}

public static void main(String[] args) {
      ConfigurableApplicationContext context = 
      new ClassPathXmlApplicationContext("Beans.xml");

      // Let us raise a start event.
      context.start();
   }

<bean id="cStartEventHandler" 
         class="com.tutorialspoint.CStartEventHandler"/>

Spring 中的自定义事件

public class CustomEvent extends ApplicationEvent{ 
   public CustomEvent(Object source) {
      super(source);
   }
   public String toString(){
      return "My Custom Event";
   }
}

public class CustomEventPublisher 
   implements ApplicationEventPublisherAware {
   private ApplicationEventPublisher publisher;
   public void setApplicationEventPublisher
              (ApplicationEventPublisher publisher){
      this.publisher = publisher;
   }
   public void publish() {
      CustomEvent ce = new CustomEvent(this);
      publisher.publishEvent(ce);
   }
}

public class CustomEventHandler 
   implements ApplicationListener<CustomEvent>{
   public void onApplicationEvent(CustomEvent event) {
      System.out.println(event.toString());
   }
}

 public static void main(String[] args) {
      ConfigurableApplicationContext context = 
      new ClassPathXmlApplicationContext("Beans.xml");    
      CustomEventPublisher cvp = 
      (CustomEventPublisher) context.getBean("customEventPublisher");
      cvp.publish();  
      cvp.publish();
   }

<bean id="customEventHandler" 
      class="com.tutorialspoint.CustomEventHandler"/>

   <bean id="customEventPublisher" 
      class="com.tutorialspoint.CustomEventPublisher"/>

Spring 框架的 AOP

Spring 框架的一个关键组件是面向切面的编程(AOP)框架。在我们开始使用 AOP 工作之前,让我们熟悉一下 AOP 概念和术语。这些术语并不特定于 Spring,而是与 AOP 有关的。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/> <!-- 开启注入注解扫描-->

    <aop:config>
        <aop:aspect id="log" ref="logging"> <!-- 定义切面 -->
            <aop:pointcut id="selectAll"
                          expression="execution(* com.li.*.*(..))"/> <!-- 定义切入点 -->
            <aop:before pointcut-ref="selectAll" method="beforeAdvice"/> <!-- 动作执行前 -->
            <aop:after pointcut-ref="selectAll" method="afterAdvice"/>  <!-- 动作执行后 -->
            <aop:after-returning pointcut-ref="selectAll"
                                 returning="retVal"
                                 method="afterReturningAdvice"/>  <!-- return 动作方法执行后 -->
            <aop:after-throwing pointcut-ref="selectAll"
                                throwing="ex"
                                method="AfterThrowingAdvice"/><!-- throw异常抛出 动作方法执行后 -->
        </aop:aspect>
    </aop:config>

    <!-- Definition for student bean -->
    <bean id="student" class="com.li.Student">
        <property name="name"  value="Zara" />
        <property name="age"  value="11"/>
    </bean>

    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.li.Logging"/>



package com.li;

public class Logging {
    /**
     * This is the method which I would like to execute
     * before a selected method execution.
     */
    public void beforeAdvice(){
        System.out.println("Going to setup student profile.");
    }
    /**
     * This is the method which I would like to execute
     * after a selected method execution.
     */
    public void afterAdvice(){
        System.out.println("Student profile has been setup.");
    }
    /**
     * This is the method which I would like to execute
     * when any method returns.
     */
    public void afterReturningAdvice(Object retVal){
        System.out.println("Returning:" + retVal.toString() );
    }
    /**
     * This is the method which I would like to execute
     * if there is an exception raised.
     */
    public void AfterThrowingAdvice(IllegalArgumentException ex){
        System.out.println("There has been an exception: " + ex.toString());
    }
}

package com.li;

public class Student {
    private Integer age;
    private String name;
    public void setAge(Integer age) {
        this.age = age;
    }
    public Integer getAge() {
        System.out.println("Age : " + age );
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        System.out.println("Name : " + name );
        return name;
    }
    public void printThrowException(){
        System.out.println("Exception raised");
        throw new IllegalArgumentException();
    }
}

        ApplicationContext context =
                new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) context.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();

Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content

上面定义了 com.tutorialspoint 中所有方法的 。让我们假设一下,你想要在一个特殊的方法之前或者之后执行你的建议,你可以通过使用真实类和方法名称替换星号(*)的切入点来定义你的切入点。

   <aop:config>
   <aop:aspect id="log" ref="logging">
      <aop:pointcut id="selectAll" 
      expression="execution(* com.tutorialspoint.Student.getName(..))"/>
      <aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
      <aop:after pointcut-ref="selectAll" method="afterAdvice"/>
   </aop:aspect>
   </aop:config>


Going to setup student profile.
Name : Zara
Student profile has been setup.
Age : 11
Exception raised
.....
other exception content

Spring 中基于 AOP 的 @AspectJ

声明一个切入点

一个切入点有助于确定连接点(即方法)关于面中的不同行为。在处理基于配置的 XML 架构时,切入点的声明有两个部分:

  • 一个切入点表达式决定了我们感兴趣的哪个方法会真正被执行。

一个切入点标签包含一个名称和任意数量的参数。方法的真正内容并不关系,并且实际上XML中它应该是空的。

package com.li;

import org.aspectj.lang.annotation.*;

@Aspect
public class Logging {
    /** Following is the definition for a pointcut to select
     *  all the methods available. So advice will be called
     *  for all the methods.
     */
    @Pointcut("execution(* com.li.*.*(..))")
    private void selectAll(){}
    /**
     * This is the method which I would like to execute
     * before a selected method execution.
     */
    @Before("selectAll()")
    public void beforeAdvice(){
        System.out.println("Going to setup student profile.");
    }
    /**
     * This is the method which I would like to execute
     * after a selected method execution.
     */
    @After("selectAll()")
    public void afterAdvice(){
        System.out.println("Student profile has been setup.");
    }
    /**
     * This is the method which I would like to execute
     * when any method returns.
     */
    @AfterReturning(pointcut = "selectAll()", returning="retVal")
    public void afterReturningAdvice(Object retVal){
        System.out.println("Returning:" + retVal.toString() );
    }
    /**
     * This is the method which I would like to execute
     * if there is an exception raised by any method.
     */
    @AfterThrowing(pointcut = "selectAll()", throwing = "ex")
    public void AfterThrowingAdvice(IllegalArgumentException ex){
        System.out.println("There has been an exception: " + ex.toString());
    }
}

Student 类不变
<aop:aspectj-autoproxy/>

    <!-- Definition for student bean -->
    <bean id="student" class="com.li.Student">
        <property name="name"  value="Zara" />
        <property name="age"  value="11"/>
    </bean>

    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.li.Logging"/>

        ApplicationContext context =
                new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) context.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();

Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content

Spring JDBC 框架概述 

JdbcTemplate 类执行 SQL 查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获 JDBC 异常并转换它们到 org.springframework.dao 包中定义的通用类、更多的信息、异常层次结构。JdbcTemplate 类的实例是线程安全配置的。所以你可以配置 JdbcTemplate 的单个实例,然后将这个共享的引用安全地注入到多个 DAOs 中。使用 JdbcTemplate 类时常见的做法是在你的 Spring 配置文件中配置数据源,然后共享数据源 bean 依赖注入到 DAO 类中,并在数据源的设值函数中创建了 JdbcTemplate。

   <bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
      <property name="username" value="root"/>
      <property name="password" value="password"/>
   </bean>

   <!-- Definition for studentJDBCTemplate bean -->
   <bean id="studentJDBCTemplate" 
      class="com.tutorialspoint.StudentJDBCTemplate">
      <property name="dataSource"  ref="dataSource" />    
   </bean>

数据访问对象(DAO)

DAO 代表常用的数据库交互的数据访问对象。DAOs 提供一种方法来读取数据并将数据写入到数据库中,它们应该通过一个接口显示此功能,应用程序的其余部分将访问它们。

package com.tutorialspoint;
import java.util.List;
import javax.sql.DataSource;
public interface StudentDAO {
   /** 
    * This is the method to be used to initialize
    * database resources ie. connection.
    */
   public void setDataSource(DataSource ds);
   /** 
    * This is the method to be used to create
    * a record in the Student table.
    */
   public void create(String name, Integer age);
   /** 
    * This is the method to be used to list down
    * a record from the Student table corresponding
    * to a passed student id.
    */
   public Student getStudent(Integer id);
   /** 
    * This is the method to be used to list down
    * all the records from the Student table.
    */
   public List<Student> listStudents();
   /** 
    * This is the method to be used to delete
    * a record from the Student table corresponding
    * to a passed student id.
    */
   public void delete(Integer id);
   /** 
    * This is the method to be used to update
    * a record into the Student table.
    */
   public void update(Integer id, Integer age);
}
public class Student {
   private Integer age;
   private String name;
   private Integer id;
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }
   public void setId(Integer id) {
      this.id = id;
   }
   public Integer getId() {
      return id;
   }
}

public class StudentMapper implements RowMapper<Student> {
   public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
      Student student = new Student();
      student.setId(rs.getInt("id"));
      student.setName(rs.getString("name"));
      student.setAge(rs.getInt("age"));
      return student;
   }
}

public class StudentJDBCTemplate implements StudentDAO {
   private DataSource dataSource;
   private JdbcTemplate jdbcTemplateObject; 
   public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      this.jdbcTemplateObject = new JdbcTemplate(dataSource);
   }
   public void create(String name, Integer age) {
      String SQL = "insert into Student (name, age) values (?, ?)";     
      jdbcTemplateObject.update( SQL, name, age);
      System.out.println("Created Record Name = " + name + " Age = " + age);
      return;
   }
   public Student getStudent(Integer id) {
      String SQL = "select * from Student where id = ?";
      Student student = jdbcTemplateObject.queryForObject(SQL, 
                        new Object[]{id}, new StudentMapper());
      return student;
   }
   public List<Student> listStudents() {
      String SQL = "select * from Student";
      List <Student> students = jdbcTemplateObject.query(SQL, 
                                new StudentMapper());
      return students;
   }
   public void delete(Integer id){
      String SQL = "delete from Student where id = ?";
      jdbcTemplateObject.update(SQL, id);
      System.out.println("Deleted Record with ID = " + id );
      return;
   }
   public void update(Integer id, Integer age){
      String SQL = "update Student set age = ? where id = ?";
      jdbcTemplateObject.update(SQL, age, id);
      System.out.println("Updated Record with ID = " + id );
      return;
   }
}
public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      StudentJDBCTemplate studentJDBCTemplate = 
      (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");    
      System.out.println("------Records Creation--------" );
      studentJDBCTemplate.create("Zara", 11);
      studentJDBCTemplate.create("Nuha", 2);
      studentJDBCTemplate.create("Ayan", 15);
      System.out.println("------Listing Multiple Records--------" );
      List<Student> students = studentJDBCTemplate.listStudents();
      for (Student record : students) {
         System.out.print("ID : " + record.getId() );
         System.out.print(", Name : " + record.getName() );
         System.out.println(", Age : " + record.getAge());
      }
      System.out.println("----Updating Record with ID = 2 -----" );
      studentJDBCTemplate.update(2, 20);
      System.out.println("----Listing Record with ID = 2 -----" );
      Student student = studentJDBCTemplate.getStudent(2);
      System.out.print("ID : " + student.getId() );
      System.out.print(", Name : " + student.getName() );
      System.out.println(", Age : " + student.getAge());      
   }

Spring 中 SQL 的存储过程

SimpleJdbcCall 类可以被用于调用一个包含 IN 和 OUT 参数的存储过程,

public class StudentJDBCTemplate implements StudentDAO {
   private DataSource dataSource;
   private SimpleJdbcCall jdbcCall;
   public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      this.jdbcCall =  new SimpleJdbcCall(dataSource).
                       withProcedureName("getRecord");
   }
   public void create(String name, Integer age) {
      JdbcTemplate jdbcTemplateObject = new JdbcTemplate(dataSource);
      String SQL = "insert into Student (name, age) values (?, ?)";
      jdbcTemplateObject.update( SQL, name, age);
      System.out.println("Created Record Name = " + name + " Age = " + age);
      return;
   }
   public Student getStudent(Integer id) {
      SqlParameterSource in = new MapSqlParameterSource().
                              addValue("in_id", id);
      Map<String, Object> out = jdbcCall.execute(in);
      Student student = new Student();
      student.setId(id);
      student.setName((String) out.get("out_name"));
      student.setAge((Integer) out.get("out_age"));
      return student;
   }
   public List<Student> listStudents() {
      String SQL = "select * from Student";    
      List <Student> students = jdbcTemplateObject.query(SQL,new StudentMapper());
      return students;
   }
}

   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      StudentJDBCTemplate studentJDBCTemplate = 
      (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");     
      System.out.println("------Records Creation--------" );
      studentJDBCTemplate.create("Zara", 11);
      studentJDBCTemplate.create("Nuha", 2);
      studentJDBCTemplate.create("Ayan", 15);
      System.out.println("------Listing Multiple Records--------" );
      List<Student> students = studentJDBCTemplate.listStudents();
      for (Student record : students) {
         System.out.print("ID : " + record.getId() );
         System.out.print(", Name : " + record.getName() );
         System.out.println(", Age : " + record.getAge());
      }
      System.out.println("----Listing Record with ID = 2 -----" );
      Student student = studentJDBCTemplate.getStudent(2);
      System.out.print("ID : " + student.getId() );
      System.out.print(", Name : " + student.getName() );
      System.out.println(", Age : " + student.getAge());      
   }

DELIMITER $$
DROP PROCEDURE IF EXISTS `TEST`.`getRecord` $$
CREATE PROCEDURE `TEST`.`getRecord` (
IN in_id INTEGER,
OUT out_name VARCHAR(20),
OUT out_age  INTEGER)
BEGIN
   SELECT name, age
   INTO out_name, out_age
   FROM Student where id = in_id;
END $$
DELIMITER ;

Spring 事务管理

局部事物 vs. 全局事务:局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。

局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。

全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。

Spring 支持两种类型的事务管理:

  • 编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。

  • 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。

Spring 事务抽象

Spring事务管理的五大属性:隔离级别传播行为是否只读事务超时回滚规则,Spring 事务抽象的关键是由 org.springframework.transaction.PlatformTransactionManager 接口定义,如下所示:

public interface PlatformTransactionManager {
//根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。
   TransactionStatus getTransaction(TransactionDefinition definition);
   throws TransactionException;
//该方法提交给定的事务和关于它的状态。
   void commit(TransactionStatus status) throws TransactionException;
//该方法执行一个给定事务的回滚。
   void rollback(TransactionStatus status) throws TransactionException;
}

TransactionDefinition 是在 Spring 中事务支持的核心接口,它的定义如下:
public interface TransactionDefinition {
//该方法返回传播行为。
   int getPropagationBehavior();
//该方法返回该事务独立于其他事务的工作的程度。
   int getIsolationLevel();
//该方法返回该事务的名称。
   String getName();
//该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成
   int getTimeout();
//该方法返回该事务是否是只读的。
   boolean isReadOnly();
}
//隔离级别
这是默认的隔离级别
TransactionDefinition.ISOLATION_DEFAULT  

表明能够阻止误读;可以发生不可重复读和虚读。 
TransactionDefinition.ISOLATION_READ_COMMITTED  

表明可以发生误读、不可重复读和虚读。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED

表明能够阻止误读和不可重复读;可以发生虚读。
TransactionDefinition.ISOLATION_REPEATABLE_READ

表明能够阻止误读、不可重复读和虚读。
TransactionDefinition.ISOLATION_SERIALIZABLE

//传播类型
支持当前事务;如果不存在当前事务,则抛出一个异常。
TransactionDefinition.PROPAGATION_MANDATORY

如果存在当前事务,则在一个嵌套的事务中执行。
TransactionDefinition.PROPAGATION_NESTED

不支持当前事务;如果存在当前事务,则抛出一个异常。	
TransactionDefinition.PROPAGATION_NEVER

不支持当前事务;而总是执行非事务性。	
TransactionDefinition.PROPAGATION_NOT_SUPPORTED

支持当前事务;如果不存在事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRED

创建一个新事务,如果存在一个事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_REQUIRES_NEW

支持当前事务;如果不存在,则执行非事务性。
TransactionDefinition.PROPAGATION_SUPPORTS

使用默认超时的底层事务系统,或者如果不支持超时则没有。	
TransactionDefinition.TIMEOUT_DEFAULT

TransactionStatus 接口为事务代码提供了一个简单的方法来控制事务的执行和查询事务状态

public interface TransactionStatus extends SavepointManager {
在当前事务时新的情况下,该方法返回 true。
   boolean isNewTransaction();

该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。
   boolean hasSavepoint();

该方法设置该事务为 rollback-only 标记。
   void setRollbackOnly();

//该方法返回该事务是否已标记为 rollback-only。
   boolean isRollbackOnly();


//该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。
   boolean isCompleted();
}

Spring 编程式事务管理

CREATE TABLE Student(
   ID   INT NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(20) NOT NULL,
   AGE  INT NOT NULL,
   PRIMARY KEY (ID)
);
CREATE TABLE Marks(
   SID INT NOT NULL,
   MARKS  INT NOT NULL,
   YEAR   INT NOT NULL
);

public interface StudentDAO {
   /** 
    * This is the method to be used to initialize
    * database resources ie. connection.
    */
   public void setDataSource(DataSource ds);
   /** 
    * This is the method to be used to create
    * a record in the Student and Marks tables.
    */
   public void create(String name, Integer age, Integer marks, Integer year);
   /** 
    * This is the method to be used to list down
    * all the records from the Student and Marks tables.
    */
   public List<StudentMarks> listStudents();
}
public class StudentMarks {
private Integer age;
   private String name;
   private Integer id;
   private Integer marks;
   private Integer year;
   private Integer sid;
    ...set and getter
}

public class StudentMarksMapper implements RowMapper<StudentMarks> {
   public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {
      StudentMarks studentMarks = new StudentMarks();
      studentMarks.setId(rs.getInt("id"));
      studentMarks.setName(rs.getString("name"));
      studentMarks.setAge(rs.getInt("age"));
      studentMarks.setSid(rs.getInt("sid"));
      studentMarks.setMarks(rs.getInt("marks"));
      studentMarks.setYear(rs.getInt("year"));
      return studentMarks;
   }
}

public class StudentJDBCTemplate implements StudentDAO {
   private DataSource dataSource;
   private JdbcTemplate jdbcTemplateObject;
   private PlatformTransactionManager transactionManager;
   public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      this.jdbcTemplateObject = new JdbcTemplate(dataSource);
   }
   public void setTransactionManager(
      PlatformTransactionManager transactionManager) {
      this.transactionManager = transactionManager;
   }
   public void create(String name, Integer age, Integer marks, Integer year){
      TransactionDefinition def = new DefaultTransactionDefinition();
      TransactionStatus status = transactionManager.getTransaction(def);
      try {
         String SQL1 = "insert into Student (name, age) values (?, ?)";
         jdbcTemplateObject.update( SQL1, name, age);
         // Get the latest student id to be used in Marks table
         String SQL2 = "select max(id) from Student";
         int sid = jdbcTemplateObject.queryForInt( SQL2 );
         String SQL3 = "insert into Marks(sid, marks, year) " + 
                       "values (?, ?, ?)";
         jdbcTemplateObject.update( SQL3, sid, marks, year);
         System.out.println("Created Name = " + name + ", Age = " + age);
         transactionManager.commit(status);
      } catch (DataAccessException e) {
         System.out.println("Error in creating record, rolling back");
         transactionManager.rollback(status);
         throw e;
      }
      return;
   }
   public List<StudentMarks> listStudents() {
      String SQL = "select * from Student, Marks where Student.id=Marks.sid";
      List <StudentMarks> studentMarks = jdbcTemplateObject.query(SQL, 
                                         new StudentMarksMapper());
      return studentMarks;
   }
}

   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");
      StudentJDBCTemplate studentJDBCTemplate = 
      (StudentJDBCTemplate)context.getBean("studentJDBCTemplate");     
      System.out.println("------Records creation--------" );
      studentJDBCTemplate.create("Zara", 11, 99, 2010);
      studentJDBCTemplate.create("Nuha", 20, 97, 2010);
      studentJDBCTemplate.create("Ayan", 25, 100, 2011);
      System.out.println("------Listing all the records--------" );
      List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
      for (StudentMarks record : studentMarks) {
         System.out.print("ID : " + record.getId() );
         System.out.print(", Name : " + record.getName() );
         System.out.print(", Marks : " + record.getMarks());
         System.out.print(", Year : " + record.getYear());
         System.out.println(", Age : " + record.getAge());
      }
   }

<!-- Initialization for data source -->
   <bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
      <property name="username" value="root"/>
      <property name="password" value="password"/>
   </bean>

   <!-- Initialization for TransactionManager -->
   <bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource"  ref="dataSource" />    
   </bean>

   <!-- Definition for studentJDBCTemplate bean -->
   <bean id="studentJDBCTemplate"
      class="com.tutorialspoint.StudentJDBCTemplate">
      <property name="dataSource"  ref="dataSource" />
      <property name="transactionManager"  ref="transactionManager" />    
   </bean>

Spring 声明式事务管理

你可以只使用注释或基于配置的 XML 来管理事务。 bean 配置会指定事务型方法。

  • 我们使用标签,它创建一个事务处理的建议,同时,我们定义一个匹配所有方法的切入点,我们希望这些方法是事务型的并且会引用事务型的建议。

  • 如果在事务型配置中包含了一个方法的名称,那么创建的建议在调用方法之前就会在事务中开始进行。

  • 目标方法会在 try / catch 块中执行。

  • 如果方法正常结束,AOP 建议会成功的提交事务,否则它执行回滚操作。
public class StudentJDBCTemplate implements StudentDAO{
   private JdbcTemplate jdbcTemplateObject;
   public void setDataSource(DataSource dataSource) {
      this.jdbcTemplateObject = new JdbcTemplate(dataSource);
   }
   public void create(String name, Integer age, Integer marks, Integer year){
      try {
         String SQL1 = "insert into Student (name, age) values (?, ?)";
         jdbcTemplateObject.update( SQL1, name, age);
         // Get the latest student id to be used in Marks table
         String SQL2 = "select max(id) from Student";
         int sid = jdbcTemplateObject.queryForInt( SQL2 );
         String SQL3 = "insert into Marks(sid, marks, year) " + 
                       "values (?, ?, ?)";
         jdbcTemplateObject.update( SQL3, sid, marks, year);
         System.out.println("Created Name = " + name + ", Age = " + age);
         // to simulate the exception.
         throw new RuntimeException("simulate Error condition") ;
      } catch (DataAccessException e) {
         System.out.println("Error in creating record, rolling back");
         throw e;
      }
   }
   public List<StudentMarks> listStudents() {
      String SQL = "select * from Student, Marks where Student.id=Marks.sid";
      List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL, 
      new StudentMarksMapper());
      return studentMarks;
   }
}

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

   <!-- Initialization for data source -->
   <bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/TEST"/>
      <property name="username" value="root"/>
      <property name="password" value="cohondob"/>
   </bean>
    
  
   <tx:advice id="txAdvice"  transaction-manager="transactionManager">
      <tx:attributes>
      <tx:method name="create"/>
      </tx:attributes>
   </tx:advice>

   <aop:config> <>
      <aop:pointcut id="createOperation" 
      expression="execution(* com.tutorialspoint.StudentJDBCTemplate.create(..))"/>
      <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/>
   </aop:config>

    <!-- 
< aop:aspect>:定义切面(切面包括通知和切点)
<aop:advisor>:定义通知器(通知器跟切面一样,也包括通知和切点)
-->

   <!-- Initialization for TransactionManager -->
   <bean id="transactionManager"
   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource"  ref="dataSource" />    
   </bean>

   <!-- Definition for studentJDBCTemplate bean -->
   <bean id="studentJDBCTemplate"  
   class="com.tutorialspoint.StudentJDBCTemplate">
      <property name="dataSource"  ref="dataSource" />  
   </bean>
</beans>

<tx:advice/> 标签来指定不同的事务性设置,<tx:advice/> 和 <tx:attributes/> 标签里的<tx:method/> 各种属性设置总结如下:

其他


1.@Configurable 可以为非Spring容器管理的对象(自己new出来的)注入依赖
2.@Autowired  //使用自动装配,默认按照byType对应的类型查找bean,如果只有一个注入,如果有多个,需要使用@Qualifier(" ") 来指定使用那个实现
3.@Resource(name="user") 以对应名字装配。name省略默认字段名字
4.@component把普通pojo实例化到spring容器中
5.@Value 将外部的值动态注入到Bean中
不通过配置文件的注入属性的情况:
1)注入普通字符串
@Value("normal") 
private String normal
2)注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName;
3)注入表达式结果 
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber;
4)注入其他Bean属性:注入beanInject对象的属性another
@Value("#{beanInject.another}")
private String fromAnotherBean;
5)注入文件资源
@Value("classpath:com/hry/spring/configinject/config.txt")
private Resource resourceFile;
6)注入URL资源
@Value("http://www.baidu.com")
private Resource testUrl;
通过配置文件的注入属性的情况:
@Value("${book.name}")
private String bookName; // 注入book.name属性
6. @Nullable
其实这个功能一般用于方法签名和方法调用。
方法返回值
@Nullable:对方法的调用没有判空会被提示。
@Notnull:方法如果可能返回null会被提示。
方法参数:
@Nullable:对参数直接使用没有判空会被提示。
@Notnull:调用时传入的参数可能为null会被提示。

Service、Controller、Repository和Component的区别
@Service用于标注业务层组件,表示定义一个bean,自动根据bean的类名实例化一个首写字母为小写的bean,
例如Chinese实例化为chinese,如果需要自己改名字则:@Service("你自己改的bean名")。   
@Controller用于标注控制层组件(如struts中的action),
@Repository用于标注数据访问组件,即DAO组件,
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。  

7.ApplicationContextAware 

在我们的web程序中,用spring来管理各个实例(bean), 有时在程序中为了使用已被实例化的bean, 通常会用到这样的代码:

ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext-common.xml");  
AbcService abcService = (AbcService)appContext.getBean("abcService");  

但是这样就会存在一个问题:因为它会重新装载applicationContext-common.xml并实例化上下文bean,如果有些线程配置类也是在这个配置文件中,那么会造成做相同工作的的线程会被启两次。一次是web容器初始化时启动,另一次是上述代码显示的实例化了一次。当于重新初始化一遍!!!!这样就产生了冗余。

解决方法

不用类似new ClassPathXmlApplicationContext()的方式,从已有的spring上下文取得已实例化的bean。通过ApplicationContextAware接口进行实现。

当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。

加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware 接口,那么在加载Spring配置文件时,会自动调用实现了ApplicationContextAware 接口中的方法:

public void setApplicationContext(ApplicationContext context) throws BeansException

方法,获得ApplicationContext对象,ApplicationContext对象是由spring注入的。

8. 利用注解进行装配

 <context:annotation-config/> <!-- 开启注入注解扫描-->

<aop:aspectj-autoproxy proxy-target-class="true"/> 通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。

当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

<context:component-scan>查找注解所标注的类,如@Component(组件),@Service(服务),@Controller(控制器),@Repository(数据仓库)。它有下面的几个属性:

back-package:标识了<context:component-scan>元素所扫描的包,可以使用一些通配符进行配置

annotation-config:<context:component-scan>元素也完成了<context:annotation-config>元素的工作,开关就是这个属性,false则关闭属性注入注解功能

name-generator:这个属性指定注册为Bean的ID生成策略,这个生成器基于接口BeanNameGenerator实现generateBeanName方法,你可以自己写个类去自定义策略。这边,我们可不显示配置,它是默认使用org.springframework.context.annotation.AnnotationBeanNameGenerator生成器,也就是类名首字符小写的策略,如Performer类,它注册的Bean的ID为performer.并且可以自定义ID,如@Component("Joy").这边简单贴出这个默认生成器的实现。

在Spring的配置文件,有时候为了分模块的更加清晰的进行相关实体类的配置,可以使用<import resource="xxx" />标签引入其他模块XML。

Spring Task标签:

task:annotation-driven标签:该标签是task命名空间中最基础的标签,用于开启定时任务和异步调用的注解支持,下该注解的几个属性:

  • scheduler,配置对应的定时任务对象,如果没有配置,默认将使用TaskScheduler实例;
  • executor,配置对应的异步执行的对象,如果没有配置,默认将使用SimpleAsyncTaskExecutor实例;
  • exception-handler,在异步执行期间,抛出的异常的实例,默认使用SimpleAsyncUncaughtExceptionHandler,抛出的异常不会被程序捕获到;
  • proxy-target-class,是否要创建CGLIB代理,默认是false,也就是创建的是基于Java接口的代理;
  • mode,异步调用的模式,默认的异步调用是通过Spring AOP来实现的,不过我们可以通过该属性指定是否需要使用Aspectj的支持,使用Spring Aop代理时还可以通过proxy-target-class属性指定是否需要强制使用CGLIB做基于Class的代理;该属性有两个选项:proxyaspectj

task:executor标签:该标签是用于对任务执行的通用配置,可用于执行不同的任务策略:同步的,异步的,使用线程池的等。通常用于异步调用,来简单看下属性:

  • id,这个不多说了,对应的executor的实例名称,通常是executor,也就是ThreadPoolTaskExecutor实例,一般用于Async异步执行的时候需要配置对应的executor;
  • pool-size,线程池中线程的数量(单个值或者范围,如5-10);如果为10,表示核心线程数是10,最大线程数也是10;如果是5-10,表示核心线程数是5,最大线程数是10;如果不指定,则默认核心线程数是1,最大线程数是Integer.MAX_VALUE;最大线程数只有在队列容量不是无限制的时候才有用;
  • queue-capacity,队列容量,如果没有指定,默认Integer.MAX_VALUE;
  • keep-alive,表示超过核心线程数的线程 在完成任务之后,处于空闲状态的时间限制,也就是说过了这段时间之后,线程会终止掉,单位是秒;为0会导致多余的线程在执行完任务后立即终止,而不需要在任务队列中执行后续工作;
  • rejection-policy,线程池中的任务队列满了以后对于新任务的处理策略:
  • ABORT 默认,抛出异常,然后不执行相应的任务;
  • DISCARD 不执行任务,也不抛出异常,也就是忽略这个任务;
  • DISCARD_OLDEST 将队列中最旧的那个任务丢弃,执行新任务;
  • CALLER_RUNS 不在新线程中执行任务,而是强制由调用者所在的线程来执行

task:scheduler标签:该标签很简单,配置项不多,是用于定时任务相关的统一的配置,来看下属性:

  1. id,用于定时调度任务的ThreadPoolTaskScheduler的bean的id,一般用于@Scheduled定时任务执行;
  2. pool-size,定时调度线程池的大小,默认是1;

task:scheduled-tasks和task:scheduled标签

task:scheduled-tasks标签及其子标签task:scheduled是用于配置具体的定时任务,该标签唯一的属性scheduler指定定时任务使用的scheduler实例,我们来看下task:scheduled标签的一些属性:

  • ref,所引用的schedule的实例id;
  • method,定时任务要调用的方法的名称;
  • cron,cron表达式,这个就不多介绍了,网上有许多详细的介绍;
  • fixed-delayfixed-rateinitial-delay
  • trigger,实现触发器接口的bean;

例如:

<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
9.PropertyPlaceholderConfigurer:PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码。PropertyPlaceholderConfigurer可以将上下文(配置文 件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值。这样的话,只需要对properties文件进 行修改,而不用对xml配置文件进行修改。用法如下:

1.编写.properties文件

/jdbc.properties 文件
jdbc.url=jdbc:mysql://66.59.208.106:3306/ds?useUnicode=true&amp;characterEncoding=utf-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

2.在.xml中引入外部文件,即.properties文件 

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<list>  
			<value>classpath:jdbc.properties</value>
			<value>classpath:inter.properties</value>
		 	<value>classpath:email.properties</value>
		 </list>
	</property>
	<property name="fileEncoding">
	   <value>UTF-8</value>
    </property>
</bean>

引入外部文件后,就可以在xml中用${key}替换指定的properties文件中的值,通常项目中都会将jdbc的配置放在properties文件中

<!-- 配置dbcp数据源 -->
<bean id="dataSourceDefault" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="${jdbc.driverClassName}" />
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
</bean>

在代码中可以配合注解使用

@Configurable
@Component
public class Config {   
    @Value("$jdbc.url}")
    private String url;
    
    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

为简化PropertyPlaceholderConfigurer的使用,Spring提供了<context:property-placeholder/>元素,启用它后,开发者便不用配置PropertyPlaceholderConfigurer对象了

<!-- 数据库配置文件位置 -->
<context:property-placeholder location="classpath:jdbc.properties" />

PropertyPlaceholderConfigurer内置的功能非常丰富,如果它未找到${xxx}中定义的xxx键,它还会去JVM系统属性(System.getProperty())和环境变量(System.getenv())中寻找。通过启用systemPropertiesMode和searchSystemEnvironment属性,开发者能够控制这一行为。context:property-placeholder大大的方便了我们数据库的配置。这样就可以为spring配置的bean的属性设置值了.

spring容器中最多只能定义一个context:property-placeholder,否则会报错:Could not resolve placeholder XXX,但如果想引入多个属性文件怎么办那,可以使用通配符:<context:property-placeholder location="classpath*:conf*.properties"/>

自定义 PropertyPlaceholderConfigurer

继承PropertyPlaceholderConfigurer 类,重写processProperties()方法.在创建bean时就会调用processProperties()方法,属性文件中设置的键值对都放在了Properties中

public class PropertyPlaceholder extends PropertyPlaceholderConfigurer {

    private static Map<String,String> propertyMap;

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
        super.processProperties(beanFactoryToProcess, props);
        propertyMap = new HashMap<String, String>();
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String value = props.getProperty(keyStr);
            propertyMap.put(keyStr, value);
        }
    }

    //自定义一个方法,即根据key拿属性值,方便java代码中取属性值
    public static String getProperty(String name) {
        return propertyMap.get(name);
    }
}

自定义了PropertyPlaceholderConfigurer类之后,在xml中配置时,配置bean中的class就必须设置为自定义的类(包路径+类名),不能再写为PropertyPlaceholderConfigurer类,否则自定义的方法不能被调用,因为根本没有创建自定义类的bean。

<bean id="propertyConfigurer" class="com.xxx.xx.PropertyPlaceholder">
	<property name="locations">
		<list>  
			<value>classpath:jdbc.properties</value>
			<value>classpath:inter.properties</value>
		 	<value>classpath:email.properties</value>
		 </list>
	</property>
	<property name="fileEncoding">
	   <value>UTF-8</value>
    </property>
</bean>

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值