您的位置:首页 > 编程语言 > Java开发

Spring揭秘:IOC与AOP学习笔记

2017-02-17 16:00 169 查看
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,也不报错;
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>  
<!-- 注意我们没有注入helloApi,所以测试时会报错 -->  
<bean id="bean"  
     class="cn.javass.spring.chapter3.bean.HelloApiDecorator"  
     dependency-check="objects">  
<property name="message" value="Haha"/>  
</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实例
定义目标接口:
package cn.javass.spring.chapter6.service;  
public interface IHelloWorldService {  
    public void sayHello();  


定义目标接口实现:
package cn.javass.spring.chapter6.service.impl;  
import cn.javass.spring.chapter6.service.IHelloWorldService;  
public class HelloWorldService implements IHelloWorldService {  
    @Override  
    public void sayHello() {  
        System.out.println("============Hello World!");  
    }  


定义切面支持类
package cn.javass.spring.chapter6.aop;  
public class HelloWorldAspect {  
       //前置通知  
    public void beforeAdvice() {  
        System.out.println("===========before advice");  
}  
//后置最终通知  
    public void afterFinallyAdvice() {  
        System.out.println("===========after finally advice");  
    }  
}
此处HelloWorldAspect类不是真正的切面实现,只是定义了通知实现的类,在此我们可以把它看作就是缺少了切入点的切面。

在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"  
        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/aop  
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd”> 
 
<bean id="helloWorldService”   class="cn.javass.spring.chapter6.service.impl.HelloWorldService”/>

<bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>
 

<aop:config>  
      <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/>  
      <aop:aspect ref="aspect">  
             <aop:before pointcut-ref="pointcut" method="beforeAdvice"/>  
             <aop:after   pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/>  
       </aop:aspect>  
</aop:config> 

</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。可以取值为no、byName、byType、constructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用
哪一种默认绑定方式。
default-dependency-check。可以取值none、objects、simple以及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



BeanPostProcessor接口:
public interface BeanPostProcessor
    Object postProcessBeforeInitialization(Object bean, String beanName) throws ➥ BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws ➥ BeansException;
}
我们看下其中一个方法的实现:



只要某个bean实现了某一个aware接口,就会被BeanPostProcessor进行处理。BeanPostProcessor的两个方法中都传入了原来的对象实例的引用,
这为我们扩展容器的对象实例化过程中的行为提供了极大的便利,我们几乎可以对传入的对象实例执行任 何的操作。
我们可以自定义BeanPostProcessor,通过实现BeanPostProcessor接口即可,但需要将自定义的BeanPostProcessor注册到容器。
除了检查标记接口以便应用自定义逻辑,还可以通过BeanPostProcessor对当前对象实例做更多的处理。比如替换当前对象实例或者字节码增强当前对象实例等。
Spring的AOP则更多地使用 BeanPostProcessor来为对象生成相应的代理对象,如org.springframework.aop.framework. autoproxy.BeanNameAutoProxyCreator。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 

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

1、ApplicationContext
BeanFactory:XmlBeanFactory实现。
ApplicationContext:三种实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring ioc aop