一、spring体系结构

1、Spring Core                Spring的核心,管理bean,提供依赖注入
2、Spring Context             Spring的上下文,BeanFactory的一个子接口
3、Spring AOP                 Spring的面向方面编程
4、Spring DAO                 Spring对JDBC的封装
5、Spring ORM                 Spring对ORM框架的支持(hibernate ...)
6、Spring Web                 Spring对Web MVC框架的支持(struts ...)
7、Spring Web MVC             Spring自身的MVC框架

二、使用Spring的实现步骤

1、创建项目并添加Spring所需jar包。(spring.jar,log4j的日志jar包,还有jakarta-commons的相关jar包)
2、创建log4j.properties文件,用来控制日志输出。
3、新建一个javaBean 。其中的属性需要定义set和get方法 。
4、创建applicationContext.xml文件,这是Spring的配置文件,可以定义上述bean以及它的属性。
5、新建一个java类,通过ApplicationContext接口的一个实现类ClassPathXmlApplicationContext读取pplicationContext.xml文件:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
然后就可以使用context的getBean(String id)方法获得指定类的实例:
HelloSping hellospring = (HelloSping)context.getBean("helloSpring");
其中helloSpring就是配置文件中bean的id 。
<bean id="helloSpring" class="com.pb.HelloSping">
接下来就可以调用获得类的方法,通过配置文件注入所需要的类,从而实现松耦合。

三、Spring框架涉及到的两种主要的设计模式

1、工厂模式  创建bean的功能由Spring工厂完成,不需要调用者创建。根据要求返回适合的类实例,工厂内的类实例其实是实现相同接口或父类的实例。
2、单态模式  对于相同id的bean的请求都返回同一个bean实例,减少系统对创建和销毁bean的开销 。
(1)把类实例设置为静态属性
(2)所有构造方法设置成私有的
(3)提供一个静态的方法获取实例属性

四、BeanFactory介绍

1、BeanFactory的作用
配置、创建以及管理bean对象(但是bean是存在于spring容器中,BeanFactory是spring容器中的一个工具)
维持Bean对象之间的依赖关系
负责Bean对象的生命周期
2、BeanFactory通常有如下方法
(1)containsBean(String beanname) //判断spring配置文件中是否有id为beanname的bean
(2)Object getBean(String beanname) //获得Bean实例
3、BeanFactory常用实现类
(1)XmlBeanFactory
(2)ApplicationContext(是BeanFactory的子接口,实现了BeanFactory的全部功能,j2ee项目推荐使用,其常用的实现类是FileSystemXmlApplicationContext和ClassPathXmlApplicationContext)
(2.1)该接口还实现了国际化的支持
(2.2)通过getResource(String resourcename)可以轻松获得Resource对象
(2.3)事件传播(自行查阅资料)
(2.4)多配置文件的加载(三种方式)
(2.4.1).第一种,使用数组参数
  ApplicationContext contex=new ClassPathXmlApplicationContext(new String["a1.xml","a2.xml"]);
(2.4.2).第二种,只用通配符
  ApplicationContext contex=new ClassPathXmlApplicationContext("a*.xml");
  但此种方法只对文件系统中的xml文件有效,针对jar包中的无效
(2.4.3).第三种,引入
  ApplicationContext contex=new ClassXmlApplicationContext("a1.xml");
  在a1.xml中
     <import resource="a2.xml"/>  执行resource路径为相对a1.xml的路径

五、ApplicationContext.xml结构分析

1、根节点<beans></beans>
2、根节点中可以包含多个<bean></bean>节点。
3、<bean>节点中有两个重要属性id(name)和class,示例如下:
<bean id="helloSpring" class="com.pb.HelloSping">
  <property name="str">
   <value>军人</value>
  </property>
  <property name="man">
   <value>林冲</value>
  </property>
</bean>
4、Bean在spring容器中有两种行为
(1)singleton  返回唯一实例
(2)non-singleton  每次请求创建新的实例
5、使用getBean(String beanid)获取bean的实例时,如果查找不到id为beanid的bean时,就会查找name为beanid的bean,如果还是查找不到就会抛出异常。但是当配置文件中的bean既没有id属性,也没有name属性,只有class属性,这时我们可以通过类的全限定名获得bean,示例如下:
<bean class="com.pb.HelloSping">
</bean>
HelloSpring hello = Context.getBean("com.pb.HelloSping");
6、指定bean的别名
<alias name="fromname" alias="toname" />
fromname指代的是bean的id或者name,toname指代的是为该bean指定的代号。该代号也可以用于getBean方法获得bean。

六、Bean的管理

1、对于基本数据类型和String类型的属性可以使用以下语句进行配置
<property name="name">
 <value>小明</value>
</property>
<property name="age">
 <value>13</value>
</property>
针对以上类型的属性,都是以String的方法注入,然后由Spring自行转换为对应的类型。
2、对于属性为对象类型的,例如Beans中其它bean类型的属性,需要使用以下配置
<property name="man">
 <ref bean="man" />
</property>
其中ref中的bean属性填入引用的bean的name或者id,除了使用bean属性之外还可以使用local属性,它们之间的区别是,bean属性可以引用不在同一个配置文件中的bean对象

3、对于集合元素类型属性的配置方法如下
(1)List   list中可以注入普通类型的值,也可以注入对象类型的值,也可以引用其它bean
   <property name="lists">
 <list>
    <value><value>
    <bean class="com.pb.SpringMan">
  ....
     </bean>
  .....
    <ref bean="" />
        </list>
   </property>
(2)Map
   <property name="maps">
 <map>
    <entry key="key1">
               <value>value1</value>
    </entry>
    <entry key="key2">
               <value>value2</value>
    </entry>
    <entry key="key3">
               <value>value3</value>
    </entry>
        </map>
   </property>
(3)Props
   <property name="props">
 <props>
    <prop key="key1">value1</prop>
    <prop key="key2">value2</prop>
    <prop key="key3">value3</prop>
        </props>
   </property>
(4)Set   set中可以注入普通类型的值,也可以注入对象类型的值,也可以引用其它bean
   <property name="sets">
 <set>
    <value><value>
    <bean class="com.pb.SpringMan">
  ....
     </bean>
  .....
    <ref bean="" />
        </set>
   </property>

4、bean的autowire属性有以下三个值
(1)、no(默认)  不使用自动绑定,bean 通过ref进行绑定
(2)、byName    根据属性的名称进行自动绑定
(3)、byType    根据属性的类型进行自动绑定
假如有一个类A,它包含一个属性b,该属性是对象类型的(类B),那么在配置文件中定义bean的时候就是如下这样:
<bean id="b" class="B"></bean>
<bean id="a" class="A" >
    <property name="b">
 <ref bean="b" />
    </property>
</bean>
这就是默认情况下的配置,即autowire="no" ,如果设置autowire="byName ",则不需要使用<property>来指定,Spring会通过属性名来寻找匹配的id的bean注入到属性中,通过设置autowire="byType "  则会通过属性的类型来寻找匹配的bean,如果找到多个匹配类型的bean则会抛出异常。

5、bean的作用域(设置bean的Scope属性)
(1)singletion 默认的,每个IOC容器只创建一个Bean实例

(2)prototype每次请求创建一个Bean实例

(3)request每次http请求创建一个实例

(4)session每次会话创建一个实例

(5)globalsession每个全局Http请求创建一个实例

6、Bean注入依赖关系后和销毁前的管理
(1)Bean注入依赖关系后
(1.1)通过指定init-method属性可以在注入依赖关系后执行一些操作,例如在Bean类中定义了一个init()方法,然后可以在配置文件对应的bean元素中添加init-method="init" 。
(1.2)通过创建类实现InitializingBean接口,然后实现里面的afterPropertiesSet()方法也可以实现上述的效果,使用该方法需要在配置文件中添加该类相应的bean,但是不推荐使用该方法,因为会造成与spring接口耦合。
(2)Bean销毁前
(2.1)通过指定destroy-method属性可以在销毁bean前执行一些操作
(2.2)通过创建类实现DisposeableBean接口,然后实现里面的destroy()方法也可以实现上述的效果

7、Bean配置的parent属性
(1)使用parent属性可以继承其它Bean的配置,可以把某些Bean的相同配置独立出来做成一个Bean,然后其它Bean继承该Bean,可以减少工作量,提高开发效率。
<bean id="women" class="com.pb.SpringWomen" parent="man1">

8、Bean注入方式
(1)属性注入  使用property
(2)构造方法注入  使用带参的构造方法,首先需要在类中定义相应的构造方法,然后需要在配置文件中指定构造方法参数的值如下:
<bean id="women1" class="com.pb.SpringWomen">
    <constructor-arg index="0">
 <value>朴信惠</value>
    </constructor-arg>
    <constructor-arg index="1">
 <value>27</value>
    </constructor-arg>
</bean>

9、针对Bean类的相关Aware接口
(1)BeanNameAware  实现该接口的Bean类可以通过实现该接口的一个方法:void setBeanName(String BeanId) 获得配置文件中对应Bean的Id或者name 。
(2)BeanFactoryAware  实现该接口的Bean可以通过对应的setBeanFactory方法(与上类似)获得BeanFactory实例
(3)ApplicationContextAware    实现该接口的Bean可以通过对应的setApplicationContext方法(与上类似)获得ApplicationContext实例

10、Bean的高级管理
(1)实现BeanPostProcessor(Bean后处理器)接口的Bean可以在Spring创建Bean实例之前和创建之后执行一些操作,需要实现该接口的两个方法:Object postProcessAfterInitialization(Object Bean,String BeanName) 和 Object postProcessBeforeInitialization (Object Bean,String BeanName) ,这两个方法有两个参数Bean为容器创建的Bean实例,BeanName是Bean配置中的id或者name,实现后一个方法的之后需要返回Bean,否则会出现空指针异常 。
(2)BeanFactoryPostProcessor(容器后处理器)可以在BeanFactory读取配置文件后,实例化Bean之前执行一些操作,需要实现以下方法
void postProcessBeanFactory(ConfigurableListableBeanFactory Congifg)  。
针对以上接口,如果是通过ApplicationContext建立的容器,只需要在配置文件中添加实现该接口的相应的Bean,容器就会自动识别并执行,而如果是通过BeanFactory建立的容器,则需要手动注册
(3)PropertyPlaceholderConfigurer 属性占位符配置器 是BeanFactoryPostProcessor的实现类,该类有如下作用:
(3.1)读取properties配置文件
(3.2)不需要打开Spring配置文件的情况下,也可以把properties配置文件中的某些值引入到Spring配置文件中
(3.3)可以配置多个配置文件信息
直接在Spring配置文件中添加相应的Bean配置即可使用
<bean id="propertypConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 <property name="locations">
  <list>
   <value>xxxx.properties</value>
   <value>xxxx.properties</value>
   .......
  </list>
 </property>
</bean>
在Spring配置文件中使用properties定义的值可以使用${key} 。使用Beanfactory创建的容器使用这些外部配置,需要在执行以下语句才能使用:          //读取配置文件
  XmlBeanFactory context = new XmlBeanFactory(new FileSystemResource("src/applicationContext.xml"));
  //创建PropertyPlaceholderConfigurer 实例
  PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
  //读取外部配置文件
  cfg.setLocation(new FileSystemResource("src/jdbc.properties"));
  //加载到Spring配置文件中
  cfg.postProcessBeanFactory(context);
(4) PropertyOverrideConfigurer  属性占位符配置器  ,使用该配置器加载的配置文件(.properties)的内容会覆盖Spring配置文件中的内容,其配置文件格式如下
BeanName.property = value
例如有如下Bean:
<bean id="man" class="com.pb.SpringMan">
<property name="name">
 <value>小明</value>
</property>
<property name="age">
 <value>13</value>
</property>
</bean>
则对应的properties配置文件格式是
man.name=王力宏
这样当在Spring中使用以下语句加载上述外部配置文件时就会覆盖原来的id为man的bean的name属性的值:
<bean id="propertypConfigurer" class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
 <property name="locations">
  <list>
   <value>xxxx.properties</value>
   <value>xxxx.properties</value>
   .......
  </list>
 </property>
</bean>
(5)自定义属性编辑器  ,由于某些特殊类型的属性值可能不能被正确转换,例如日期类型,所以需要自定义属性编辑器来解决这类问题
(5.1)、自定义属性编辑器需要继承PropertyEditorSupport类,并重写其setAsText(String text)方法,其中参数text代表需要转换的值。实现该方法时注意需要使用this.setValue(Object o )来完成转换后的内容赋值,否则转换了也没有用。参考代码如下:
 public void setAsText(String arg0) throws IllegalArgumentException {
  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  try {
   this.setValue(sdf.parse(arg0));
  } catch (ParseException e) {
   e.printStackTrace();
  }
 }
(5.2)、在Spring配置文件中添加如下配置:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
    <entry key="java.util.Date">
  <bean class="com.pb.ParseDate">
   <property name="format">
    <value>yyyy-MM-dd</value>
   </property>
  </bean>
    </entry>
 </map>
    </property>
</bean>

上述配置起作用的关键是<entry key="java.util.Date">,这个相当于过滤器,把属性类型为java.util.Date的属性配置都使用com.pb.ParseDate进行处理 。

11、AOP基本概念
(1)AOP全称是 aspect oriented programming  面向方面编程
(2)AOP的作用是把代码中的公共功能部分抽离处理,减少代码的复杂度,使得代码作用统一,例如对于异常的处理等功能就可以抽取出来。
(3)AOP术语:
Aspect 当需要时,把它放到应用程序之上,当不需要时,把它从应用程序中抽离
Advice 是Aspect的具体实现
Joinpoint  Aspect在应用程序执行时加入业务流程的时机
Pointcut  指定某个Aspect在哪些Pointcut时被穿插到应用程序之上
Target  一个advice被应用的对象或目标对象
Instruction  为已编译完成的类,在执行时动态加入一些方法,而不用修改或者增加任何代码
Weave  被应用到对象之上的过程
(4)Spring的AOP技术使用Java代码实现,而不是AOP语言
(5)Spring不支持属性成员的Joinpoint,因为这会影响Java的封装特性。

12、Advice的具体应用
(1)MethodBeforeAdvice接口   实现该接口,重写该接口的void before(Method arg0, Object[] arg1, Object arg2)方法,就可以实现在指定的bean的所有方法执行之前都执行一次before方法。具体使用步骤如下:
(1.1)、定义Bean接口(必须)
(1.2)、建立实现Bean接口及其方法的bean
(1.3)、建立实现MethodBeforeAdvice接口的类,以及重写before方法。
(1.4)、在Spring配置文件中加入以下配置:
<bean id="before" class="com.pb.MethodBefordAdvice" />  <!--实现了MethodBeforeAdvice接口的类-->
<bean id="he" class="com.pb.Hello" />  <!--目标bean-->
<bean id="helloproxy" class="org.springframework.aop.framework.ProxyFactoryBean">  <!-- ProxyFactoryBean代理类-->
 <property name="proxyInterfaces"> 
  <value>com.pb.IHello</value>
 </property>
    <property name="target">
     <ref bean="he"></ref>
    </property>
    <property name="interceptorNames">
     <list>
      <value>before</value>
     </list>
    </property>
</bean>

(1.5)、使用代理类的bean的id获得IHello接口,然后调用Hello的方法:
IHello h = (IHello)context.getBean("helloproxy");
h.sayHello("陈豪");
h.sayGoodBye("陈豪");
--------------运行结果------------------
执行sayHello方法前!
你好:陈豪
执行sayHello方法前!
陈豪,再见!!

(2)AfterReturningAdvice接口 其中的方法是void afterReturning(Object arg0, Method arg1, Object[] arg2,Object arg3) throws Throwable
作用与上相反,配置方法与上类似。

(3)MethodInterceptor接口,其中的方法是Object invoke(MethodInvocation arg0) throws Throwable ,实现该接口可以同时实现上述两个接口的功能,即在执行方法前和方法后执行一些操作。该方法需要返回一个Object对象,使用MethodInvocation 对象(即arg0)的proceed()方法可以获得,调用该方法proceed()相当于把执行权交回给目标类的方法,所以在调用该方法前的代码属于BeforeAdvice,而在调用该方法后的方法属于AfterAdvice ,该接口也称为AroundAdvice。

(4)ThorwsAdvice 接口  该接口没有定义任何方法,我们可以自行创建,方法名和参数不能够随便,以下提供了可供参考的方法:
public void afterThrowing(Exception ex)
public void afterThrowing(RemoteException)
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
以上方法会在目标bean发生异常的时候执行。

(5)基于XML Schema 创建Spring Advice
使用该方法创建的Advice不需要实现特定的接口,可以直接在Spring配置文档中配置好了即可使用,但是要满足以下要求:
(5.1)、配置文件中需要使用<aop>标签,所以需要在XML文件头中添加相应配置:
增加命名空间:xmlns:aop="http://www.springframework.org/schema/aop"
增加xsi:schemaLocation配置:http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
(5.2)、<aop:config>配置格式如下所示:
 <bean id="hello" class="com.pb.Hello" />
 <bean id="advicer" class="com.pb.HelloAdvicer" />
 <aop:config> 
      <aop:pointcut id="pointcut" expression="execution(* c*..H*.say*(..))  and args(user)"/> 
        <aop:aspect ref="advicer"> 
            <aop:before pointcut-ref = "pointcut" method="beforeAdvice(java.lang.String)" arg-names="user"/> 
            <aop:after pointcut="execution(* com.pb..*.sayHello(..))" method="afterAdvice"/> 
            <aop:after-returning pointcut="execution(* com.pb..*.sayHello(..))"  
                                 method="afterReturningAdvice" 
                                 arg-names="value" 
                                 returning="value"/> 
            <aop:after-throwing pointcut="execution(* com.pb..*.sayHello(..))" 
                               method="afterThrowingAdvice" 
                                arg-names="e" 
                               throwing="e"/> 
            <aop:around pointcut="execution(* com.pb..*.sayHello(..))" 
                        method="aroundAdvice"
                        /> 
       </aop:aspect> 
    </aop:config> 
(5.2.1)其中aop:pointcut 为切入点 其属性expression需要使用execution表达式,以这个(execution(* c*..H*.say*(..))为例说明,第一个*固定(不知道为什么),然后c*..代表以c开头类的包名可以直接使用*..代表任意包,第三个H*.代表以H开头的类名或接口名,可以直接使用*.代表任意类或接口,第三个部分是say*表示以say开头的方法名,不能直接使用*代表方法名,第四部分是(..)代表具有任意参数的方法,还可以使用具体类型(String),或者多个参数(String,Integer,..),还可以使用自定义类型(com.pb.Hello) 。可以使用简单模式实现上述效果(execution(* say*(..))
(5.2.2)<aop:aspect ref="advicer">  中ref属性指代Advice类的实现类
(5.2.3)<aop:aspect> </aop:aspect>包含的配置就是各种对应的Advice方法。具体配置参考api文档 。
(5.2.4)Advice的实现类定义如下:
public class HelloAdvicer {
 public void beforeAdvice(String user){
  System.out.println("执行sayhello方法前:"+user);
 }
 
 public void afterAdvice(){
  System.out.println("执行sayhello方法后");
 }
 
 public void afterReturningAdvice(){
  System.out.println("这是Advicer类中的afterReturningAdvice方法");
 }
 
 public void afterThrowingAdvice(){
  System.out.println("这是Advicer类中的afterThrowingAdvice方法");
 }
 
 public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
  System.out.println("这是Advicer类中的aroundAdvice方法");
  return pjp.proceed();
 
 }
}
(5.2.5)程序入口关键的代码如下:
ApplicationContext context = new FileSystemXmlApplicationContext("src/applicationContext.xml");
  IHello hello = (IHello)context.getBean("hello");
  hello.sayHello("朴信惠");
-----------运行结果---------------
执行sayhello方法前:朴信惠
这是Advicer类中的aroundAdvice方法
朴信惠,你好
执行sayhello方法后
这是Advicer类中的afterReturningAdvice方法

(6)基于注解Annotation的Advice   使用注解实现的Advice也是不需要实现特定接口就可以实现上述的Advice功能,但是需要满足以下条件:
(6.1)建立一个普通类作为Advice,添加注解如下:
 @Aspect
 public class AspectJ {
 
 @Pointcut("execution(* Hello.sayHello(..))")
 public void log(){
  
 }
 
 @Before(value="log()")
 public void startLog(){
  System.out.println("访问方法前!!");
 }
 
 @After(value="log()")
 public void endLog(){
  System.out.println("访问方法后!!");
 }
}
其中@Aspect表示这是一个Aspect类,@Pointcut定义切入点,@Before定义前方法,@After定义后方法
(6.2)配置文件中需要添加如下语句(注意使用到aop标签的要求):
 <aop:aspectj-autoproxy />
 <bean id="aspectj" class="com.pb.AspectJ" />
 <bean id="hello" class="com.pb.Hello" />
(6.3)程序入口关键代码:
  IHello h = (IHello)context.getBean("hello");
  h.sayHello("陈豪");
  h.sayGoodBye("陈豪");
------------运行结果------------
访问方法前!!
你好:陈豪
访问方法后!!
陈豪,再见!!
(7)结合pointcut和Advice的实现类,使用该方法配置是为了更加精确的指定切入advice的方法,是对前四种方法的一个功能加强
(7.1)NameMatchMethodPointcutAdvisor  最基本的PointcutAdvicer
(7.1.1)在Spring配置文件中加入以下配置即可实现功能:
<bean id="NameMatch" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
     <property name="mappedName">
      <value>say*</value>  <!--可以指定只有say开头的方法调用advice-->
     </property>
     <property name="advice">
      <ref bean="advicer"/>  <!--此处指定使用的Advice(before,after,around,throw)-->
     </property>
</bean>
---------------以下是对代理bean的修改-----------
 <bean id="helloproxy" class="org.springframework.aop.framework.ProxyFactoryBean">
 <property name="proxyInterfaces">
  <value>com.pb.IHello</value>
 </property>
    <property name="target">
     <ref bean="hello"></ref>
    </property>
    <property name="interceptorNames">
     <list>
      <value>NameMatch</value>   <!--此处使用上述配置中的bean的id-->
     </list>
    </property>
(7.1.2)程序入口代码不变
(7.2)RegexpMethodPointcutAdvisor  使用正则表达式来定义Pointcut
(7.2.1)与上述实现步骤类似,但是配置文件中需要作出下面的修改:
(7.2.2)正则表达式语法:
         表1.常用的元字符
代码              说明
.            匹配除换行符以外的任意字符
\w           匹配字母或数字或下划线或汉字
\s           匹配任意的空白符
\d           匹配数字
\b           匹配单词的开始或结束
^            匹配字符串的开始
$            匹配字符串的结束

表2.常用的限定符
代码/语法 说明
  *      重复零次或更多次
  +      重复一次或更多次
  ?      重复零次或一次
 {n}     重复n次
 {n,}    重复n次或更多次
 {n,m}   重复n到m次
(7.2.3)正则表达式示例:^.*(say)+.*$  这个代表targe中say开头的方法

(8)动态代理实现AOP           使用该方法可以不配置Spring配置信息,即可实现AOP
(8.1)需要实现InvocationHandler接口以及实现其invoke方法 。关键代码如下:
private Object targe;   //targe指代的是目标类的实例
 //获取代理类实例的方法
 public Object bind(Object targe){
  this.targe = targe;
  return  Proxy.newProxyInstance(targe.getClass().getClassLoader(),
   targe.getClass().getInterfaces(), this);
 }
 //实现AOP的核心方法
 @Override
 public Object invoke(Object arg0, Method method, Object[] arg2)
   throws Throwable {
  Object result = null;
  System.out.println("目标方法前执行");
  result = method.invoke(targe, arg2);
  System.out.println("目标方法后执行");
  return result;
 } 
(8.2)程序入口核心代码:
  LogHandler log = new LogHandler();
  IHello he = (IHello)log.bind(new Hello());  //Hello是实现IHello接口的实现类
  he.sayHello("朴信惠");
--------------执行结果-------------------
目标方法前执行
朴信惠,你好
目标方法后执行

AOP编程小结:目前学习的几种AOP编程实现几乎都是依靠代理类的 。

13、使用Spring实现JDBC操作数据库
准备条件:
Spring所需jar包,Oracle14.jar
(1)通过Spring配置建立一个数据源类型的Bean,通过引用该配置可以为DAO注入DataSource属性,在该配置中需要指定数据库驱动,url,用户名和密码等信息,示例如下:
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
   <value>jdbc:oracle:thin:@localhost:1521:orcl</value>
  </property>
  <property name="username">
   <value>zzl</value>
  </property>
  <property name="password">
   <value>zzl</value>
  </property>
</bean>
(2)建立一个接口IUserDao ,并建立一个用于操作数据库的方法,为什么需要定义接口而不直接使用建立类,当然直接建立类也是可以实现该操作的,但是定义接口可以使得业务类与Dao类解耦,而业务类只与接口耦合,那样只需要修改配置文件就可以有多种实现,示例如下:
public interface IUserDao{
 public void find();
}
(3)建立一个类实现IUserDao接口,其中需要定义一个DataSource类型的属性以及其set方法,用于注入DataSource使用,同时实现接口的方法,示例如下:
public class UserDaoImpl implements IUserDao{
 private DataSource source;             //Spring的注入不会直接注入到Bean的属性,所以该属性不是必须的

public DataSource getSource() {
  return source;
 }

//该方法才是Spring注入的核心,Spring是把注入的内容放到方法的参数了,要怎么利用参数是我们决定的
 public void setSource(DataSource source) {          
  this.source = source;
 }

@Override
 public void find() {
  Connection conn = null;
  PreparedStatement statement=null;
  ResultSet rs=null;
  try {
   conn = source.getConnection();
   statement = conn.prepareStatement("select * from house_type");
   rs = statement.executeQuery();
   while(rs.next()){
    System.out.println(rs.getInt("id")+":"+rs.getString(2));
   }
  } catch (SQLException e) {
   e.printStackTrace();
  }finally{
   try {
    if(rs!=null){
     rs.close();
    }
    if(statement!=null){
     statement.close();
    }
    if(conn!=null){
     conn.close();
    }
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }
 }

}
(4)编写程序入口代码,示例如下:
  ApplicationContext context = new FileSystemXmlApplicationContext("src/applicationContext.xml");
  IUserDao dao = (IUserDao)context.getBean("dao");
  dao.find();
(5)编写Spring配置文件:
 <bean id="dao" class="com.pb.UserDaoImpl">
  <property name="source">     <!--该name的作用是为了寻找对应的set方法,而不是属性-->
   <ref bean="datasource"/>
  </property>
 </bean>

14、使用Spring的JdbcTemplate对象简化JDBC编程步骤
(1)修改原来的实现IUserDao接口的方法,新建一个JdbcTemplate的属性以及setSource(DataSource source)方法,核心代码如下所示:
 private JdbcTemplate jdbctemplate ;
 
 public void setSource(DataSource source) {
  this.jdbctemplate = new JdbcTemplate(source);  //通过Datasource对象获得JdbcTemplate对象
 }
(2)修改原来的find()方法,核心代码如下所示:
  List list = jdbctemplate.queryForList("select * from house_type");  //通过JdbcTemplate对象的方法获得结果集合
  Iterator iterator = list.iterator(); 
  while(iterator.hasNext()){
   Map map =(Map)iterator.next();//集合中的对象都是Map类型,一个map对象包含数据库的一行记录
   int i = ( (BigDecimal)map.get("id") ).intValue();  //数据如果是长整型、浮点型和整型的都需要转换
   String name = (String)map.get("name");
   System.out.println(i+":"+name);
  }
(3)配置文件和程序入口代码不变 。

15、JdbcTemplate对象的方法介绍
(1)针对查询语句常用的方法有:List queryForList(String sql);
(2)针对DDL语句的方法是:void execute(String sql)
(3)针对插入和更新语句的方法是:int update(String sql)
(4)针对聚合函数count的方法是int queryForInt(String sql);
(5)针对带参数的Sql语句可以使用?占位符,例如:
void insert(int id,String name){
    jdbctemplate.update("insert into user values(?,?)",new Object[]{id,name});
}
(6)使用batchUpdate方法进行批量操作,该方法需要两个参数
(6.1)String sql 需要执行批量操作的Sql语句,例如:insert into user values(?,?);
(6.2)BatchPreparedStatementSetter类型的参数集合,BatchPreparedStatementSetter 是一个接口,因此可以通过实现内部类的方法来得到该参数,需要实现该接口的两个方法,示例如下:
BatchPreparedStatementSetter bps  = new BatchPreparedStatementSetter() {
   //作用是根据下标index获得和设置参数
   @Override
   public void setValues(PreparedStatement ps, int index) throws SQLException {
    ps.setInt(1, (Integer)Ids.get(index));
    ps.setString(2, (String)names.get(index));
    
   }
   //作用是返回批量操作的次数
   @Override
   public int getBatchSize() {
    return count;
   }
       };
(6.3)定义好上述两个参数就可以调用batchUpdate方法 。

17、RdbmsOperation抽象类的子类(它的子类都是线程安全的,用于针对对象进行数据库编程,类似于hibernate)
(1)SqlFunction 用于执行Sql函数,存储过程或者聚集函数的类。通过继承该类可以实现指定数据源的相关操作,步骤如下:
(1.1)新建一个类继承SqlFunction,并建立一个有参构造函数,示例如下:
 public UserFunction(DataSource ds){
     //可以使用?占位符来定义参数,然后需要使用setTypes方法来确定参数的类型,具体实现参考第二个方法
     super(ds,"select count(*) from house_type");
  compile();   //保证当前操作可以被运行
 }
(1.2)利用Dao类的setDatasource方法的实例化一个UserFunction类,示例如下:
 public void setSource(DataSource source) {
   func = new UserFunction(source);
   upd = new UserUpdate(source);
   que = new UserQuery(source);
 }
(1.3)调用SqlFunction的run()方法就可以执行指定的Sql语句select count(*) from house_type,示例如下:
 public int countNum(){
  return func.run();
 }

(2)SqlUpdate
(2.1)新建一个类继承SqlUpdate,并建立一个有参构造函数,示例如下:
 public UserUpdate(DataSource ds){
     //可以使用?占位符来定义参数,然后需要使用setTypes方法来确定参数的类型
     super(ds,"insert into house_type values(?,?)");
  int[] types ={Types.INTEGER,Types.VARCHAR};
  this.setTypes(types);
  compile();   //保证当前操作可以被运行
 }
(2.2)利用Dao类的setDatasource方法的实例化一个UserUpdate类,示例如下:
 public void setSource(DataSource source) {
   func = new UserFunction(source);
   upd = new UserUpdate(source);
   que = new UserQuery(source);
 }
(2.3)调用SqlUpdate的update()方法就可以执行指定的Sql语句insert into house_type values(?,?),示例如下:
 public int countNum(){
  return upd.update(new Object[]{7001,"好房子没蚊子"});
 }

(3)MappingSqlQuery
(3.1)新建一个类继承MappingSqlQuery,并建立一个有参构造函数,与上述两方法不同的是,该类还有一个方法需要实现,示例如下:
 public UserQuery(DataSource ds){
       //可以使用?占位符来定义参数,然后需要使用setTypes方法来确定参数的类型,具体实现参考第二个方法
  super(ds, "select * from house_type");
  compile(); //保证当前操作可以被运行
 }

@Override
 protected Object mapRow(ResultSet rs, int rownum) throws SQLException {     //把结果集的每行记录包装成一个类并返回
  HouseType type = new HouseType();
  type.setId(rs.getInt("id"));       
  type.setName(rs.getString("name"));
  return type;
 }
(3.2)利用Dao类的setDatasource方法的实例化一个UserUpdate类,示例如下:
 public void setSource(DataSource source) {
   func = new UserFunction(source);
   upd = new UserUpdate(source);
   que = new UserQuery(source);
 }
(3.3)调用MappingSqlQuery的execute()方法就可以执行指定的Sql语句,获得的是一个List集合,示例如下:
 public List findtype(){
  return que.execute();
 }

最后在Spring配置文件中添加该Dao类的配置,然后就可以在主程序中获得该Dao的实例,并调用其方法获得结果。(推荐通过定义Dao接口实现上述功能)。

18、Spring和struts整合步骤(Action通过spring的Ioc注入)
(1)建立一个web工程,导入Spring和struts整合所需jar包。
(2)修改web.xml文件,添加struts和spring所需配置,如下所示:
<filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

<filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
   
     <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>/WEB-INF/classes/applicationContext.xml</param-value> <!--该语句指向spring配置文件所在的路径-->
    </context-param>
 
    <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
(3)添加struts的配置文件,需要在原来的配置文件内容基础上添加以下内容:
<constant name="struts.objectFactory" value="spring" />  <!--指定Action工厂使用spring-->

<action name="hello" class="userAction">  <!--class使用spring配置文件中Action的Bean的id-->
.....
</action>
(4)添加Spring的配置文件,里面需要定义一个Action的Bean
(5)Action类需要继承ActionSupport,该ActionSupport并不是Struts提供的那个,而是Spring提供的,该类所在包如下所示:
import org.springframework.web.struts.ActionSupport;
(6)完成以上步骤既可以完成Spring和struts的整合。

19、Spring和hibernate整合步骤(SessionFactory由Spring的Ioc注入)
(1)在Spring的配置文件中添加如下内容:
<!--第一个Bean是用来获得datasource的-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
   <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
   <value>jdbc:oracle:thin:@localhost:1521:orcl</value>
  </property>
  <property name="username">
   <value>zzl</value>
  </property>
  <property name="password">
   <value>zzl</value>
  </property>
</bean>
<!--第二个Bean是用来获得SessionFactory的-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="datasource"/>
  </property>
  <property name="mappingResources"> <!--此处指定表的映射文件路径,是一个list集合-->
   <list>
    <value>com/pb/hibernate/entity/Grade.hib.xml</value>
   </list>
  </property>
</bean>
<!--通过以上两步就可以获得sessionfactory了,然后Spring就可以为dao注入,Spring和hibernate的整合主要实现的就是sessionfactory注入--> 
<bean id="dao" class="com.pb.hibernate.dao.HibernateDAO">
 <property name="factory">
  <ref bean="sessionFactory"/>
 </property>
</bean>
(2)通过上述的配置内容,我们就可以在原来的hibernate项目中修改,使得原来通过hibernate.cfg.xml配置文件获得的SessionFactory,修改为通过Spring注入。

20、Spring事务管理
(1)编程式事务管理  特点:管理的粒度比较细,可以细化到一行或几行语句,但是需要把代码陷入到业务类中,增加耦合。该事务管理方式常用的有两种方式:
(1.1)PlatformTransactionManager 接口 
步骤:
(1.1.1)在Spring配置文件中添加 PlatformTransactionManager 接口的实现类 DataSourceTransactionManager的bean ,如下所示:
 <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="datasource"/>   <!--需要使用到datasource-->
 </bean>
(1.1.2)使用以下语句开启一个事务:
   ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
------------------------------------------------------------------------------------------------------------------
  PlatformTransactionManager ptm = (PlatformTransactionManager)context.getBean("TransactionManager");
  DefaultTransactionDefinition dtf = new DefaultTransactionDefinition();   //实例化一个事务
  dtf.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  //定义事务的行为
  TransactionStatus ts = ptm.getTransaction(dtf);  //开启事务

(1.1.3)把需要事务管理的业务代码放在try...catch块中,当发生异常时就使用事务管理对象(ptm)的回滚事务(ts),具体代码如下:
  try{
   DataSource ds = (DataSource) context.getBean("datasource");
   JdbcTemplate jt = new JdbcTemplate(ds);
   jt.execute("insert into house_type values(15,'一房三厅')");
   jt.execute("insert into house_type values(16,'一房三厅')");
   ptm.commit(ts);  //用于提交操作
  }catch(Exception e){
   ptm.rollback(ts);  //用于回滚操作
   e.printStackTrace();
  }

(1.2)事务模板TransactionTemplate
步骤:
(1.2.1)在Spring配置文件中添加 PlatformTransactionManager 接口的实现类 DataSourceTransactionManager的bean ,和上述第一步一样。
(1.2.2)使用TransactionTemplate封装PlatformTransactionManager接口的实现类:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
------------------------------------------------------------------------------------------------------------------
  PlatformTransactionManager ptm = (PlatformTransactionManager)context.getBean("TransactionManager");
  TransactionTemplate tt = new TransactionTemplate(ptm);
------------------------------------------------------------------------------------------------------------------
  tt.execute(new TransactionCallbackWithoutResult() { TransactionCallbackWithoutResult的内部类
   @Override
   protected void doInTransactionWithoutResult(TransactionStatus arg0) {
    jt.execute("insert into house_type values(17,'一房三厅')");
    jt.execute("insert into house_type values(18,'一房三厅')");
   }
  });
(2)声明式事务管理  特点:使用Spring配置文件即可完成事务管理,但是管理只能细化到方法。耦合度低。该类事务管理也可以分成两类:
(2.1)配置代理bean实现事务管理(AOP)
步骤:
(2.1.1)在Spring配置文件中添加如下代理类配置:
 <bean id="ProxyPersonDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="proxyInterfaces">  <!--此处指定目标类所实现的接口,代理方式都是强制依赖接口编程吗?-->
   <value>com.pb.IPersonDao</value>
  </property>
  <property name="target" ref="persondao"/><!--此处指定目标类-->
  <property name="transactionManager">
   <ref bean="transactionManager"/>
  </property>
  <property name="transactionAttributes">
   <props>
    <!--此处指定该事务管理针对类的哪些方法(key),以及使用哪种行为-->
    <prop key="find*">PROPAGATION_REQUIRED</prop>
   </props>
  </property>
 
 </bean>        
(2.1.2)获得该接口的代理实例,并调用方法即可:
  ApplicationContext context = new FileSystemXmlApplicationContext("src/applicationContext.xml");
  IPersonDao dao = (IPersonDao)context.getBean("ProxyPersonDao");
  /****
  数据库操作....
  *****/
(2.2)基于 XML Schema的声明式事务管理
步骤:
(2.2.1)由于需要使用aop和tx的标签,所以要在Spring配置文件头中添加相应的命名空间:
 增加两个命名空间:
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
 在xsi:schemaLocation中添加下面的语句
   http://www.springframework.org/schema/aop
                 http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
(2.2.2)该事务管理方法是需要借助spring内置的事务管理类(例如:DataSourceTransactionManager),所以需要添加相关类的配置:
 <!--省略dataSource的配置-->
  ..........
 <!--省略dataSource的配置-->

<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="datasource"/>
 </bean>
 
 <tx:advice id="txAdvice" transaction-manager="TransactionManager">
  <tx:attributes>
   <tx:method name="find*" propagation="REQUIRED"/><!--指定针对的方法以及使用的事务行为-->
  </tx:attributes>
 </tx:advice>
 <aop:config>
  <aop:pointcut id="pc" expression=" execution(* com.pb.PersonDaoImpl*.f*(..))"/> <!--指定切入点-->
  <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
 </aop:config>
(2.2.3)使用该方法不需要借助代理类,所以直接通过业务类的bean获得实例也能实现事务管理的效果。

(2.3)基于注解的事务管理
步骤:
(2.3.1)在Spring配置文件中添加以下内容:
 <!--省略dataSource的配置-->
  ..........
 <!--省略dataSource的配置-->
 <!--省略TransactionManager的配置,参考上述内容-->
  ..........
 <!--省略TransactionManager的配置-->
        <tx:annotation-driven transaction-manager="TransactionManager"/><!--指定所使用的事务管理类-->
(2.3.2)在所需要事务管理的业务类中加上注解,如下所示:
@Transactional
public class PersonDaoImpl1 implements IPersonDao{
  ....省略部位内容.....

@Transactional(propagation=Propagation.REQUIRED)
 public void find(DataSource ds){
  .....省略方法中内容......
 }
}
(2.3.3)完成配置。

引用:
Spring中Propagation类的事务属性详解:

1.PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

2.PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

3.PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。

4.PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。

5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6.PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7.PROPAGATION_NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

21、事务属性(行为)的分类
(1)传播行为  参考spring文件夹中截图
(2)隔离级别  参考spring文件夹中截图
(3)只读提示
(4)事务超时期限

22、Spring实现计划任务
(1)通过继承TimerTask类实现计划任务,步骤如下(关键在Spring配置):
(1.1)创建一个类继承TimerTask,需要实现其中的run()方法,该方法中的内容就是需要定时执行的代码。
(1.2)配置Spring,添加如下内容:
 <!--该bean就是继承了TimerTask的类-->
 <bean id="update" class="com.pb.BatchUpdate">
 </bean>

<!--以下是TimerTask的定义-->
 <bean id="ScheduledTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <property name="delay">  <!--该参数是启动Spring后多久开始执行计划任务(单位:毫秒)-->
   <value>1000</value>
  </property>
  <property name="period"> <!--该参数是定义任务之间延迟的时间(单位:毫秒)-->
   <value>2000</value>
  </property>
  <property name="timerTask"> <!--该参数指定定义有定时执行方法的类-->
   <ref bean="update"/>
  </property>
 </bean>
 <!--以下是TimerTask工厂的定义-->
 <bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean" >
  <property name="scheduledTimerTasks">
   <list>
    <ref bean="ScheduledTimerTask"/>
   </list>
  </property>
  <property name="daemon">
   <value>false</value>
  </property>
 </bean>
(1.3)启动Spring即可实现计划任务
(2)通过MethodInvokingTimerTaskFactoryBean类的配置实现计划任务,步骤如下:
(2.1)在Spring配置文件中添加如下的内容:
 <!--以下是定义了哪个类的哪个方法作为计划任务-->
 <bean id="MethodInvoking"
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
  <property name="targetObject">
    <ref bean="update"/>
  </property>
  <property name="targetMethod">
   <value>run</value>
  </property>
 </bean>
 <!--以下是TimerTask的定义-->
  ......与上基本相同.....
  <property name="timerTask"> <!--该参数指定定义有定时执行方法的类-->
   <ref bean="MethodInvoking"/> <!--此处修改为上述定义的MethodInvoking-->
  </property>
 <!--以下是TimerTask的定义-->
 <!--以下是TimerTask工厂的定义-->
  ....与上同.....
 <!--以下是TimerTask工厂的定义-->
(2.2)包含计划任务方法的类不需要继承TimerTask 。直接启动Spring即可。

(3)使用Quartz实现计划任务,分为传统型和精确型两类(注意:必须导入quartz-all-1.6.0.jar和jta.jar)
(3.1)传统型,实现步骤如下:
(3.1.1)创建一个类继承QuartzJobBean类,并重写void executeInternal(JobExecutionContext arg0)方法,里面的内容就是计划任务需要执行的代码。
(3.1.2)在Spring配置文件中添加如下内容:
 <!--以下是任务类的配置-->
 <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
   <value>com.pb.BatchUpdate</value>  <!--引用的类全限定名,注意不需要另外定义计划任务类的bean-->
  </property>
  <property name="jobDataAsMap">  <!--类中所包含的属性需要注入的值-->
   <map>
    <entry  key="command">
     <value>更新</value>
    </entry>
   </map>
  </property>
 </bean>
 <!--以下是触发器的配置-->
 <bean id="SimpleTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerBean" >
  <property name="jobDetail">   <!--指定使用的任务类-->
   <ref bean="jobDetail"/>
  </property>
  <property name="startDelay">  <!--指定开启Spring后多少时间执行计划(单位:毫秒)-->
   <value>1000</value>
  </property>
  <property name="repeatInterval">  <!--指定任务之间的重复执行时间间隔(单位:毫秒)-->
   <value>2000</value>
  </property>
 </bean>
 <!--任务工厂配置-->
 <bean id="beanFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
   <list>
    <ref bean="SimpleTriggerBean"/>
   </list>
  </property>
 </bean>
(3.1.3)完成配置,启动Spring即可。
(3.2)精确型,实现步骤如下:
(3.2.1)与3.1.1的内容相同
(3.2.2)和3.1.2内容基本相同,但是关于触发器的配置修改为如下所示:
 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">  <!--指定使用的任务类-->
   <ref bean="jobDetail"/>
  </property>
  <property name="cronExpression"> <!--指定执行的时间,例如:每天的01:40:00-->
   <value>00 40 01 * * ? </value> 
  </property>
 </bean>
(3.2.3)完成配置,启动Spring即可。

(3.3)使用模板实现quartz
步骤如下:
(3.3.1)不需要继承任何类,只需要定义一个普通的类即可,其中包含计划任务执行的方法。
(3.3.2)在Spring配置文件中添加如下语句:
 <!--配置任务类-->
 <bean id="update" class="com.pb.BatchUpdate">
  <property name="command">
   <value>插入</value>
  </property>
 </bean>
 <!--配置MethodInvokingJobDetailFactoryBean,用于指定计划任务调用的类以及方法-->
 <bean id="MethodInvoking"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
  <property name="targetObject">
    <ref bean="update"/>
  </property>
  <property name="targetMethod">
   <value>executeInternal</value>
  </property>
 </bean>
-----------------------------------------------------------------------------------------------
 <!--以下是触发器的配置-->
  ....内容基本和前面方法一样...
  注意以下内容:
  <property name="jobDetail">   <!--指定使用的任务类-->
   <ref bean="MethodInvoking"/>  <!--这里使用上述定义的MethodInvokingJobDetailFactoryBean的id-->
  </property>
 <!--以下是触发器的配置-->
 <!--任务工厂配置-->
  ....内容基本和前面方法一样...
 <!--任务工厂配置-->
(3.3.3)完成配置,启动Spring即可。

Spring 技术总结相关推荐

  1. 社群:加入 Spring 技术学习群

    入群须知 为了构建高质量的技术交流社群 减少低质量内容的产出,建议入群前先阅读本须知 了解本社群所涉及的内容主题与相关群规 社群主题:Spring技术 适合人群: 已经具备Java基础能力(若还没有J ...

  2. 华章揭秘系列精品图书(《Android应用开发揭秘》、《GWT揭秘》、《Spring技术内幕》)...

    Android应用开发揭秘(国内首本基于Andriod 2.0的经典著作,5大专业社区一致鼎力推荐!互动网畅销排行榜第1名) 作者:杨丰盛 出版社:机械工业出版社 标准书号:978-7-111-291 ...

  3. 《Spring技术内幕(第2版)》PDF 国内经典分析spring源代码

    书名:Spring技术内幕(第2版) 作者: 计文柯 出版社: 机械工业出版社 副标题: 深入解析Spring架构与设计原理 出版年: 2012-2 页数: 399 定价: 69.00元 装帧: 平装 ...

  4. java布道师_我和 Spring 技术布道师的一天

    摘要: 先介绍一下故事的5位主人公. Josh Long 龙之春:Spring 技术布道师,撰写过5部著作,录制过3部畅销的培训视频,是一位开源软件贡献者. Spencer Gibb:Spring 技 ...

  5. 资深架构师推荐Spring技术内幕:深入了解Spring的底层机制

    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...

  6. Spring技术内幕

    Spring技术内幕--深入解析Spring架构与设计原理 图 书 内 容 本书是Spring领域的问鼎之作,由业界拥有10余年开发经验的资深Java专家亲自执笔!Java开发者社区和Spring开发 ...

  7. Spring技术内幕:设计理念和整体架构概述

    为什么80%的码农都做不了架构师?>>>    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深 ...

  8. spring技术的通俗理解

    spring技术内容太过庞杂,笔者尚在学习之中,以下理解难免有错漏之处,还请大神们提点. 一提到spring技术,随之而来的必然就是这样3个名词:控制反转IOC,依赖注入DI.面向切面编程AOP.但是 ...

  9. Spring技术内幕总结

    最近一段时间看了一下书名叫<Spring技术内幕--深入解析Spring架构与设计原理>,这么熟将spring写的非常的经典,将spring所有的内幕技术解析的非常的详细,自己只是粗略的大 ...

  10. 一次Spring技术的面试,被面试官怼的怀疑人生。。

    Spring无论在Java生态系统,还是在就业市场,是绝对的王者.面试出镜率之高,投产规模之广,无出其右.随着技术的发展,Spring 从往日的 IoC 框架,已发展成 Cloud Native 基础 ...

最新文章

  1. 这套“人造肌腱”装备,可保护你的老腰|Science子刊
  2. python与excel结合-python3与Excel的完美结合
  3. 【转】eclipse android 设置及修改生成apk的签名文件 -- custom debug keystore
  4. Ribbon源码解析(一)
  5. ubuntu xfce下面两个终端合并为一个终端
  6. python bool值要注意的一些地方
  7. IT 人永远不老,老程序员价值何在?
  8. SQL Server 锁升级阈值
  9. 表格中复制后出现空格_软件应用在Excel表格中怎样批量删除空格?
  10. Linux综合练习——课件分发
  11. Nginx之rewrite配置
  12. 2019年,有远见的程序员都在关注这些硬核公众号
  13. Oracle10g安装在RHEL AS 3
  14. Java使用HttpURLConnection上传文件
  15. 2016-7-3 linux学习笔记
  16. 成功自我暗示三大规律
  17. 谷歌“Adobe Flash Player已被屏蔽”的解决办法
  18. mysql sniffer下载_Gitee 极速下载
  19. linux系统忘记密码之破解密码
  20. antd mobile在微信公众号开发中使用笔记

热门文章

  1. JS正则匹配所有中文字符
  2. 电脑远程控制,win7远程控制电脑_Win7系统怎么远程控制别人的电脑
  3. linux创建、删除、编辑、文件,目录,权限等
  4. python提取邮件里面的发件人
  5. 项目一 51单片机蓝牙控制继电器
  6. 基于C++实现(MFC界面)家谱管理系统【100010005】
  7. 手机登录群晖出现ssl证书不可信_教你申请免费SSL证书以及安全访问群晖的方法教程...
  8. 利用VBA快速整合多个excel文件
  9. SpringBoot从入门到精通教程(二十七)- @Valid注解用法详解+全局处理器Exception优雅处理参数验证用法
  10. 怎么使用gulp压缩文件、图片