Spring揭秘:IOC与AOP学习笔记

时间:2022-12-26 12:55:23

Book2: Spring揭秘(245/673)


########################################################################################

######## 20161207~20161208 ##############################

########################################################################################

1、我搭建的第一个Spring测试环境。导入了以下6个jar包,参考:http://www.importnew.com/13246.html

  • org.springframework.aop-3.2.9
  • org.springframework.aspects-3.2.9
  • org.springframework.beans-3.2.9
  • org.springframework.context-3.2.9
  • org.springframework.context.support-3.2.9
  • org.springframework.core-3.2.9
  • org.springframework.expression-3.2.9
  • commons-logging-1.2


2、实例:

package com.importnew;

public classHelloWorld {

    private String message;

    public voidsetMessage(String message){

        this.message  = message;

    }

    public String getMessage(){

        return this.message;

    }

    public voidprintMessage(){

        System.out.println("Your Message : "+ message);

    }

}



配置:

<?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.importnew.HelloWorld">

           <property name="message" value="Hello World!"/>

    </bean>

</beans>


package com.importnew;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

        HelloWorld obj = (HelloWorld) context.getBean("helloWorld");

        obj.printMessage();

    }

}


另外一种方式:

<bean id=" bean"  class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>

BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean2.xml”);

HelloApi bean = beanFactory.getBean("bean", HelloApi.class);

bean.sayHello();

 

3、注释:

      1、两个最基本最重要的包是 org.springframework.beans 和 org.springframework.context

     这两个包中的代码为Spring的反向控制 特性(也叫作依赖注射)提供了基础。

      2、ApplicationContext是BeanFactory的子类。

      3、org.springframework.beans.factory.xml.XmlBeanFactory是接口BeanFactory的一个实现

      4、在ApplicationContext的诸多实现中,有三个经常用到:

     ClassPathXmlApplicationContext:从类路径中的XML文件载入上下文定义信息,把上下文定义文件当作类路径资源。

     FileSystemXmlApplicationContext:从文件系统中的XML文件载入上下文定义信息。

     XmlWebApplicationContext:从Web系统中的XMl文件载入上下文信息。

4、aop的原理即是java的动态代理?


5、配置简写

让我们来总结一下依赖注入配置及简写形式,其实我们已经在以上部分穿插着进行简化配置了:

    一、构造器注入:

     1)常量值

       简写:<constructor-arg index="0" value="常量"/>

       全写:<constructor-arg index="0"><value>常量</value></constructor-arg>

     2)引用

       简写:<constructor-arg index="0" ref="引用"/>

       全写:<constructor-arg index="0"><ref bean="引用"/></constructor-arg>

    二、setter注入:      

       1)常量值

        简写:<property name="message" value="常量"/>

        全写:<property name="message"><value>常量</value></ property>

       2)引用

        简写:<property name="message" ref="引用"/>

        全写:<property name="message"><ref bean="引用"/></ property>

       3)数组:<array>没有简写形式

       4)列表:<list>没有简写形式

       5)集合:<set>没有简写形式

       6)字典

          简写:<map>

             <entry key="键常量" value="值常量"/>

             <entry key-ref="键引用" value-ref="值引用"/>

            </map>

         全写:<map>

             <entry><key><value>键常量</value></key><value>值常量</value></entry>

             <entry><key><ref bean="键引用"/></key><ref bean="值引用"/></entry>

           </map>




################################################################################

######## 20161209 ###########################################

################################################################################


1、延迟初始化Bean: lazy-init

  延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。

  <bean id="helloApi"  class="cn.javass.spring.chapter2.helloworld.HelloImpl"    lazy-init="true"/>  

不建议使用:除非是那种消耗资源大,且不常用的bean。


2、depends-on


3、依赖检查

   上一节介绍的自动装配,很可能发生没有匹配的Bean进行自动装配,如果此种情况发生,只有在程序运行过程中发生了空指针异常才能发现错误,如果能提前发现该多好啊,这就是依赖检查的作用。依赖检查:用于检查Bean定义的属性都注入数据了,不管是自动装配的还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。Spring3+也不推荐配置方式依赖检查了,建议采用Java5+ @Required注解方式,测试时请将XML schema降低为2.5版本的,和自动装配中“autodetect”配置方式的xsd一样。

依赖检查有none、simple、object、all四种方式,接下来让我们详细介绍一下:

一、none:默认方式,表示不检查;

二、objects:检查除基本类型外的依赖对象,配置方式为:dependency-check="objects",此处我们为HelloApiDecorator添加一个String类型属 

      性“message”,来测试如果有简单数据类型的属性为null,也不报错;

  1. <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>  
  2. <!-- 注意我们没有注入helloApi,所以测试时会报错 -->  
  3. <bean id="bean"  
  4.      class="cn.javass.spring.chapter3.bean.HelloApiDecorator"  
  5.      dependency-check="objects">  
  6. <property name="message" value="Haha"/>  
  7. </bean>  

  注意由于我们没有注入bean需要的依赖“helloApi”,所以应该抛出异常UnsatisfiedDependencyException,表示没有发现满 

  足的依赖:

三、simple:对基本类型进行依赖检查,包括数组类型,其他依赖不报错;配置方式为:dependency-check="simple",以下配置中没有注入message属性,所以会抛出异常:

四、all:对所以类型进行依赖检查,配置方式为:dependency-check="all",如下配置方式中如果两个属性其中一个没配置将报错。



#####################################################################################

######## 20161210 #############################################

#####################################################################################

Bean的作用域

singleton:指“singleton”作用域的Bean只会在每个Spring IoC容器中存在一个实例,而且其完整生命周期完全由Spring容器管理。对于所有获取该Bean的操作Spring容器将只返回同一个Bean。

prototype:即原型,指每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。



######################################################################################

######## 20161211 ###### 201612114 ###########################

######################################################################################

1、AOP:低耦合的新增一些功能。

  • 用于横切关注点的分离和织入横切关注点到系统;比如上边提到的日志等等;
  • 完善OOP;
  • 降低组件和模块之间的耦合性;
  • 使系统容易扩展;
  • 而且由于关注点分离从而可以获得组件的更好复用。

  AOP代理就是AOP框架通过代理模式创建的对象,Spring使用JDK动态代理或CGLIB代理来实现,Spring缺省使用JDK 

  动态代理来实现,从而任何接口都可别代理,如果被代理的对象实现不是接口将默认使用CGLIB代理,不过CGLIB代理当

  然也可应用到接口。


2、需要新增一个包啦:





###################################################################################

######## 20170216 #########################################

###################################################################################

3、aop基本概率

 在进行AOP开发前,先熟悉几个概念:

  • 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP中表示为“在哪里干”
  • 切入点(Pointcut):选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为“在哪里干的集合”
  • 通知(Advice):在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为“干什么”;
  • 方面/切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是通知、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为“在哪干和干什么集合”;
  • 引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在AOP中表示为“干什么(引入什么)”
  • 目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为“被通知对象”;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中表示为“对谁干”
  • AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。
  • 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。

接下来再让我们具体看看Spring有哪些通知类型:

  • 前置通知(Before Advice):在切入点选择的连接点处的方法之前执行的通知,该通知不影响正常程序执行流程(除非该通知抛出异常,该异常将中断当前方法链的执行而返回)。
  • 后置通知(After Advice):在切入点选择的连接点处的方法之后执行的通知,包括如下类型的后置通知:
    • 后置返回通知(After returning Advice):在切入点选择的连接点处的方法正常执行完毕时执行的通知,必须是连接点处的方法没抛出任何异常正常返回时才调用后置通知。
    • 后置异常通知(After throwing Advice): 在切入点选择的连接点处的方法抛出异常返回时执行的通知,必须是连接点处的方法抛出任何异常返回时才调用异常通知。
    • 后置最终通知(After finally Advice): 在切入点选择的连接点处的方法返回时执行的通知,不管抛没抛出异常都执行,类似于Java中的finally块。
  • 环绕通知(Around Advices):环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等等。




4、基于schema的aop实例

定义目标接口:

  1. package cn.javass.spring.chapter6.service;  
  2. public interface IHelloWorldService {  
  3.     public void sayHello();  


定义目标接口实现:

  1. package cn.javass.spring.chapter6.service.impl;  
  2. import cn.javass.spring.chapter6.service.IHelloWorldService;  
  3. public class HelloWorldService implements IHelloWorldService {  
  4.     @Override  
  5.     public void sayHello() {  
  6.         System.out.println("============Hello World!");  
  7.     }  


定义切面支持类

  1. package cn.javass.spring.chapter6.aop;  
  2. public class HelloWorldAspect {  
  3.        //前置通知  
  4.     public void beforeAdvice() {  
  5.         System.out.println("===========before advice");  
  6. }  
  7. //后置最终通知  
  8.     public void afterFinallyAdvice() {  
  9.         System.out.println("===========after finally advice");  
  10.     }  
  11. }

此处HelloWorldAspect类不是真正的切面实现,只是定义了通知实现的类,在此我们可以把它看作就是缺少了切入点的切面。


在XML中进行配置

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans  xmlns="http://www.springframework.org/schema/beans"  
  3.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.         xmlns:aop="http://www.springframework.org/schema/aop"  
  5.         xsi:schemaLocation="  
  6.            http://www.springframework.org/schema/beans  
  7.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.            http://www.springframework.org/schema/aop  
  9.            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd”> 
  10.  
  11. <bean id="helloWorldService”   class="cn.javass.spring.chapter6.service.impl.HelloWorldService”/>
  12. <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>  
  13. <aop:config>  
  14.       <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/>  
  15.       <aop:aspect ref="aspect">  
  16.              <aop:before pointcut-ref="pointcut" method="beforeAdvice"/>  
  17.              <aop:after   pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/>  
  18.        </aop:aspect>  
  19. </aop:config> 
  20. </beans>


切入点使用<aop:config>标签下的<aop:pointcut>配置,expression属性用于定义切入点模式,默认是AspectJ语法,“execution(* cn.javass..*.*(..))”表示匹配cn.javass包及子包下的任何方法执行。切面使用<aop:config>标签下的<aop:aspect>标签配置,其中“ref”用来引用切面支持类的方法。


前置通知使用<aop:aspect>标签下的<aop:before>标签来定义,pointcut-ref属性用于引用切入点Bean,而method用来引用切面通知实现类中的方法,该方法就是通知实现,即在目标类方法执行之前调用的方法。pointcut和pointcut-ref:二者选一,指定切入点;

最终通知使用<aop:aspect>标签下的<aop:after >标签来定义,切入点除了使用pointcut-ref属性来引用已经存在的切入点,也可以使用pointcut属性来定义,如pointcut="execution(* cn.javass..*.*(..))",method属性同样是指定通知实现,即在目标类方法执行之后调用的方法。


5、aop原理

静态代理的缺陷:每次都需要创建一个代理类,不实用。

aop的两种实现原理:

  1.动态代理:通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

     动态代理要求:被代理的对象必须实现接口。即必须针对接口编程,因此有了CGLIB。  

  2.CGLIB:通过实现net.sf.cglib.proxy.MethodInterceptor接口(或者其扩展接口:MethodInterceptor接口)

    CGLIB要求:无法对final方法进行覆写。

Spring揭秘第九章第一代aop需仔细阅读,搞清楚原理。



###################################################################################

######## 20170217 ##########################################

###################################################################################

6、aop:基于@AspectJ的aop

   1)使用@Aspect将POJO声明为切面;

   2)使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他匹配的方法但参数类型不一致的将也是不匹配的,通过

        argNames = "param"指定了将把该匹配的目标方法参数传递给通知同名的参数上;

   3)使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;

   4)配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;

   5)需要将切面注册为Bean,如“aspect”Bean;

   6)测试代码完全一样。

通配符:

 * :匹配任何数量字符;

 .. :(两个点)匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。

 + :匹配指定类型的子类型;仅能作为后缀放在类型模式后边。


实例:

声明切面:使用@Aspect

@Aspect()

public class Aspect{


     @Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)”, 

                   argNames = "param”)  

     public void beforePointcut(String param) {}


     @Before(value = "beforePointcut(param)”, 

                 argNames = "param”)  

     public void beforeAdvice(String param) {  

         System.out.println("===========before advice param:" + param); 

     }


     @AfterReturning(value = "beforePointcut(param)”,  

                             argNames="retVal”, 

                             returning="retVal”)  

      public void afterReturningAdvice(Object retVal) {  

           System.out.println("===========after returning advice retVal:" + retVal);  

      }  


     @AfterThrowing( value="execution(* cn.javass..*.sayAfterThrowing(..))",  

                            argNames="exception”,

                            throwing="exception")  

     public void afterThrowingAdvice(Exception exception) {  

            System.out.println("===========after throwing advice exception:" + exception);  

     }


     @After(value="execution(* cn.javass..*.sayAfterFinally(..))")  

     public void afterFinallyAdvice() {  

          System.out.println("===========after finally advice");  

     }  

 

     //看他的参数:ProceedingJoinPoint pjp,与其他通知不同

     @Around(value="execution(* cn.javass..*.sayAround(..))")  

     public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {  

          System.out.println("===========around before advice");  

          Object retVal = pjp.proceed(new Object[] {"replace"});  

          System.out.println("===========around after advice");  

          return retVal;  

      }  

}

切面需在配置文件中声明,Spring就能自动识别并进行aop方面的配置:

<bean id="aspect" class="……Aspect"/> 



###############################################################################

######## 20170221 ##################################

###############################################################################

7、BeanFactory的XML之旅

<beans>作为所有<bean>的“统帅”,它拥有相应的属性(attribute)对所辖的<bean>进行统一 的默认行为设置,包括如下几个:

default-lazy-init。其值可以指定为true或者false,默认值为false。用来标志是否对所有的<bean>进行延迟初始化。

default-autowire。可以取值为nobyNamebyTypeconstructor以及autodetect默认值为no,如果使用自动绑定的话,用来标志全体bean使用 哪一种默认绑定方式。

default-dependency-check。可以取值noneobjectssimple以及all,默认值为none,即不做依赖检查。

default-init-method。如果所管辖的<bean>按照某种规则,都有同样名称的初始化方法的 话,可以在这里统一指定这个初始化方法名。

default-destroy-method。与default-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法。

其中default-lazy-init是配置全局的懒加载模式,也可以单独在每个bean上进行配置:<bean id="lazy-init-bean" class="..." lazy-init="true"/>

其中default-autowire是配置全局的自动配置模式,也可以单独在每个bean上进行配置。

自动绑定和手动明确绑定各有利弊。自动绑定的优点有如下两点:

(1) 某种程度上可以有效减少手动敲入配置信息的工作量。

(2) 某些情况下,即使为当前对象增加了新的依赖关系,但只要容器中存在相应的依赖对象,就不需要更改任何配置信息。

自动绑定的缺点有如下几点:

(1) 自动绑定不如明确依赖关系一目了然。我们可以根据明确的依赖关系对整个系统有一个明确的认识,但使用自动绑定的话,就可能需要在类定义以及配置文件之间,甚至各个配置 文件之间来回转换以取得相应的信息。

(2) 某些情况下,自动绑定无法满足系统需要,甚至导致系统行为异常或者不可预知。根 据类型(byType)匹配进行的自动绑定,如果系统中增加了另一个相同类型的bean定义,那么 整个系统就会崩溃;根据名字(byName)匹配进行的自动绑定,如果把原来系统中相同名称的 bean定义类型给换掉,就会造成问题,而这些可能都是在不经意间发生的。

(3) 使用自动绑定,我们可能无法获得某些工具的良好支持,比如Spring IDE。

 

标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例; 而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope。以下三种定义都代表是singleton的scope的:

<bean id="mockObject1" class="...MockBusinessObject"/>

<bean id="mockObject1" class="...MockBusinessObject" singleton="true"/>

<bean id="mockObject1" class="...MockBusinessObject" scope="singleton"/


针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新 生成一个新的对象实例给请求方:

<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>

<bean id="mockObject1" class="...MockBusinessObject" scope="prototype"/>


###############################################################################

#######20170222 ##################################

###############################################################################

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

容器启动阶段

Configuration MetaData

||  加载并分析配置信息

BeanDefinition

||  注册

BeanDefinitionRegister

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Bean实例化阶段

getBean


BeanFactoryPostProcessor:干预容器启动阶段

该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改,

比如修改其中bean定义的某些属性,为bean定义增加其他信息等。配置信息,修改一些配置参数

具体用其两个实现类:

org.springframework.beans.factory.config.PropertyPlaceholderConfigurer

org.springframework.beans.factory.config.PropertyOverrideConfigurer



Spring揭秘:IOC与AOP学习笔记

BeanPostProcessor接口:

public interface BeanPostProcessor

    Object postProcessBeforeInitialization(Object bean, String beanName) throws ➥ BeansException;

    Object postProcessAfterInitialization(Object bean, String beanName) throws ➥ BeansException;

}

我们看下其中一个方法的实现:

Spring揭秘:IOC与AOP学习笔记

只要某个bean实现了某一个aware接口,就会被BeanPostProcessor进行处理。BeanPostProcessor的两个方法中都传入了原来的对象实例的引用,

这为我们扩展容器的对象实例化过程中的行为提供了极大的便利,我们几乎可以对传入的对象实例执行任 何的操作。

我们可以自定义BeanPostProcessor,通过实现BeanPostProcessor接口即可,但需要将自定义的BeanPostProcessor注册到容器。

除了检查标记接口以便应用自定义逻辑,还可以通过BeanPostProcessor对当前对象实例做更多的处理。比如替换当前对象实例或者字节码增强当前对象实例等。

Spring的AOP则更多地使用 BeanPostProcessor来为对象生成相应的代理对象,如org.springframework.aop.framework. autoproxy.BeanNameAutoProxyCreator。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

 


###############################################################################

#######20170222 1818 ##################################

###############################################################################


1、ApplicationContext

BeanFactory:XmlBeanFactory实现。

ApplicationContext:三种实现