热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringIOC和AOP

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。  1、控制反转(IOC)依赖注入(DI):     在传统的程序设计中,当调用者需要被调用者的

Spring是一个轻量级的控制反转IoC)和面向切面(AOP)的容器框架。 

 1、控制反转(IOC)/依赖注入(DI):

        在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC),为什么称为反转呢?反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中。
创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),所以其实依赖注入和控制反转是表达是同一个概念,只是描述的角度不同而已,依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
       Spring 解决了一个非常关键的问题他可以让你把对象之间的依赖关系转而用配置文件来管理,也就是他的依赖注入机制。而这个注入关系在一个叫 Ioc 容器中管理,那 Ioc 容器中就是被 Bean 包裹的对象。Spring 正是通过把对象包装在 Bean 中而达到对这些对象管理以及一些列额外操作的目的。

        用图例来说明一下,先看没有IoC/DI的时候,常规的A类使用C类的示意图,如图1所示:

技术分享

图1  常规A使用C示意图

当有了IoC/DI的容器后,A类不再主动去创建C了,如图2所示:

技术分享

图2  A类不再主动创建C

而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中,如图3所示:

技术分享 

图3  有IoC/DI容器后程序结构示意图

 
1.1、Spring容器

        Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立 Bean 之间的依赖关系。Spring 的 IoC 容器在完成这些底层工作的基 础上,还提供了 Bean 实例缓存、生命周期管理、Bean 实例代理、事件发布、资源装载等 高级服务。 Spring提供了多种容器的实现,分为两类,Bean工厂(由org.springframework.beans.factory.BeanFactory 接口定义)(最简单的容器,提供了基础的依赖注入支持) 和应用上下文(由org.springframework.context.ApplicationContext接口定义)(建立在Bean工厂基础之上,提供了系统架构服务,如从属性文件中读取文本信息,向相关的事件监听器发布事件)。

 1.1.1、Bean Factory

       Bean 工厂(com.springframework.beans.factory.BeanFactory)是 Spring 框架最核心的接 口,它提供了高级 IoC 的配置机制。 Bean工厂,不像其他工厂模式的实现,只能分发一种类型的对象,Bean工厂可以创建和分发各种类型的Bean。Bean工厂在实例化这些对象的时候还创建它们之间的关联关系(DI)。Bean工厂还要参与Bean的生命周期中,调用用户定义好的初始化和销毁方法。

        Spring中有几种BeanFactory的实现方式,最常用的是org.springframework.beans.factory.xml.XmlBeanFactory.要创建XmlBeanFactory需要传递一个org.springframework.core.io.Resource实例给构造函数。
        BeanFactory  factory = new XmlBeanFactory(new FileSystemResource("c:/beans.xml"));
        MyBean myBean = (MyBean)factory.getBean("myBean");

       bean工厂延迟载入所有的单实例Bean,直到getBean()方法被调用的时候Bean才被创建。

 1.1.2、ApplicationContext

       建立在Bean工厂基础之上,提供了系统架构服务,如从属性文件中读取文本信息,向注册为监听器的Bean发送事件。ApplicationContext 的 主 要 实 现 类 是ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统 中装载配置文件, 除了提供的一些附加的功能外,与BeanFactory 另一个重要的区别是关于单实例载入:bean工厂延迟载入所有的单实例Bean,直到getBean()方法被调用的时候Bean才被创建。上下文,会在上下文启动后预载入所有的单实例bean。

1.2、Bean的装配

1.2.1、使用XML Bean的装配

定义Car实现类

 

public class Car {
  private String color ;
  private Engine engine
  
  public Car() {}
 
  public Car(String color, Engine engine) {
    this.engine =  engine;
    this.color = color
  }
  
  public void drive() {
    System.out.println("hu hu hu …..");
  }
   public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public Engine getEngine() {
        return engine;
    }
    public void setEngine(Engine engine) {
        this.engine = engine;
    }
}

构造函数注入


     //普通类型
     //其他bean,


可以注入内部bean,参见下面的setter注入。
 

setter方法注入

//普通类型
     //其他bean

  
//普通类型
      //注入内部类
          


自动装配:

显式的装配会导致大量的XML,Spring自动装配,只需要设置自动装配的中的autowire属性

 
Spring提供了四种自动装配类型:

  • byName:在容器中寻找和需要自动装配的属性名相同的Bean(或ID)。如果没有找到这个属性就没有装配上。
  • byType:在容器中寻找一个与自动装配的属性类型相同的Bean。如果没有找到这个属性就没有装配上。如果找到超过一个则抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。
  • constructor:在容器中查找与需要自动装配的Bean的构造函数参数一致的一个或多个Bean。如果存在不确定Bean或构造函数容器会抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。
  • autodetect:首先尝试使用constuctor来自动装配,然后使用byType方式。不确定性的处理与constructor方式和byType方式一样,都抛出org.springframework.beans.factory.UnsatisfiedDependencyException异常。

 

       Spring 默认情况下bean不会被自动装配的,但可以通过在spring配置文件的根元素中设置default autowire,就可以将所有的Bean设置为自动装配。SpringBean自动装配的过程中很容易出现不确定性,这些不确定性会导致程序无法运行。那么在我们的实际应用中要避免出现装配的不确定性。避免装配的不确定性的最好的方法就是使用显示装配和自动装配的混合方法。对有二义性的Bean使用显示装配,对没有二义性的Bean使用自动装配。
 
Bean范围化:
 
Spring默认所有的bean都是单一的。但为使得每次都能产生一个新的Bean实例,可以声明Bean的scope属性为prototype。这样当注入此bean时候,都会是不同的实例。
 
Spring范围化选项:

  • singleton 定义Bean的范围为每个Spring容器一个实例(默认值)。
  • Prototype 允许Bean可以多次被实例化(使用一次创建一个实例)。
  • request  Bean的范围是HTTP请求。只有在使用有web能力的spring上下文时才有效。
  • session  Bean的范围是HTTP会话。只有在使用有web能力的spring上下文时才有效。
  • global-session Bean的范围是HTTP全局会话。只有在portlet上下文才有效。


初始化和销毁Bean的时候执行一些动作,spring提供了两种方式
1、用init-method 和 destroy-method参数申明,如果在中申明这个两个参数,可以为上下文中所有的bean定义初始化和销毁时执行的方法。
2、类继承InitializingBean和DisposableBean接口

1.2.2、基于注解的Bean的装配

在一个稍大的项目中,如果组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找以及维护起来也不太方便。 而且基于XML的Bean的装配,Bean的定义信息和Bean实现类是分离的,采用基于注解的配置方式时,Bean定义信息是通过在Bean实现类上标注解实现。

  • @Service用于标注业务层组件
  • @Controller用于标注控制层组件
  • @Repository用于标注数据访问组件,即DAO组件

 

  • @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 


 Spring2.5为我们引入了组件自动扫描机制,他在类路径下寻找标注了上述注解的类,并把这些类纳入进spring容器中管理。
 

applicationContext.xml:

webmvc-config.xml:

ps.

 

因为applicationContext是mvc context的父容器,mvc context可以引用applicationContext的bean,而applicationContext无法引用到mvc的bean,

 

spring查找bean,会现在当前context中查找,如果没有满足的,再到父容器查找,

 

applicationContext是在web.xml中配置的ContentLoader监听器启动的,当xml启动时加载,并按照一个约定的key放在java的ServletContext中,然后mvc 的servlet初始化时,先从ServletContext中按照约定的key取出来,以它为父容器,去创建mvc的容器。

@Service注解的类不能在springmvc.xml(对应crm中的webmvc-config.xml)中扫描,因为springmvc.xml与applicationContext.xml不是同时加载,如果不进行这样的设置,那么,spring就会将所有带@Service注解的类都扫描到容器中,等到加载applicationContext.xml的时候,会因为容器已经存在Service类,使得cglib将不对Service进行代理,直接导致的结果就是在applicationContext 中的事务配置不起作用,发生异常时,无法对数据进行回滚。



 自动装配:

 @Autowired
    @Autowired是Spring 提供的,需导入
    Package:org.springframework.beans.factory.annotation.Autowired;
    只按照byType 注入。
 @Resource
    @Resource默认按 byName 自动注入,是J2EE提供的, 需导入Package:  
    javax.annotation.Resource;
    @Resource有两个中重要的属性:name和type ,而Spring将@Resource注解的name属性解析为bean的
    名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用
    type属性时则使用 byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用by
    Name自动注入策略。
 
    @Resource装配顺序 
  (1). 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常;
  (2). 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常;
  (3). 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;
  (4). 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
   

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入。
 
总结:基于XML方式的Bean的装配适合于Bean的实现类来源于第三方类库,如DataSource,JdbcTemplate等,因无法在类中标注注解,而基于注解的配置,较适合于当前项目中开发的Bean实现类。

2、面向切面编程(AOP):

       在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。Spring的一个关键的组件就是 AOP框架。 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善。

技术分享

技术分享


AOP几个术语:
通知(Advice):定义了切面,如Security,定义了切面的“什么”和“何时”
连接点(Joinpoint):     程序在执行过程中可以插入切面的点
切入点(Pointcut):通知要织如入的一个或多个连接点,定义了切面的“何地”
切面(Aspect):通知和切入点的结合
引入(Introduction):允许我们向现有的类添加新方法和属性,让现有的类具有新的行为和状态
目标(Target):被通知的对象,如果没有AOP,这个对象必须包含自己的主要逻辑和交叉业务的逻辑。
代理(Proxy):是向目标对象应用通知之后被创建的对象,对于客户端来说目标对象和代理对象是没有区别的。
织入(Weaving):把切面应用到目标对象来创建代理对象的过程。


三个重要的AOP框架
AspectJ(http://eclipse.org/aspectj)

 AspectJ是语言级的AOP实现,2001年由Xerox PARC的AOP小组发布,目前版本已经更新到1.6。AspectJ扩展了Java语言,定义了AOP语法,能够在编译期提供横切代码的织入,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。主页位于http://www.eclipse.org/aspectj。 
 

JBoss AOP (http://labs.jboss.com/portal/jobssaop/index.html)

 2004年作为JBoss应用程序服务器框架的扩展功能发布,可以从这个地址了解到JBoss AOP的更多信息:http://www.jboss.org/products/aop。 

Spring AOP (http://www.springframework.org )

Spring AOP使用纯Java实现,它不需要专门的编译过程,不需要特殊的类装载器,它在运行期通过代理方式向目标类织入增强代码。Spring并不尝试提供最完整的AOP实现,相反,它侧重于提供一种和Spring IoC容器整合的AOP实现,用以解决企业级开发中的常见问题。在Spring中,我们可以无缝地将Spring AOP、IoC和AspectJ整合在一起。 


下面主要介绍Spring AOP,Spring对AOP的支持具有以下的4种情况:经典的基于代理的AOP(各版本Spring),@AspectJ注解驱动的切面(仅Spring 2.0),纯POJO切面(仅Spring 2.0),注入式的AspectJ切面(各版本Spring)。

定义目标类

public interface Performer {
  public void perform() throws PerformanceException;
}
  
public class Singer implements Performer {
  private String name;
  private String song;
   
  public Singer(String name) {
    this(name, null);
  }
   
  public Singer(String name, String song) {
    this.name = name;
    this.sOng= song;
  }
   
  public void perform() throws PerformanceException {
    if(sOng== null) {
      throw new PerformanceException(name + " doesn't have a song to sing.");
    }
    System.out.println(name + " is singing " + song);
  }
   
  public void setSong(String song) {
    this.sOng= song;
  }

定义观众类

public class Audience {
  public Audience() {}
  public void watchPerformance(ProceedingJoinPoint jp) {
    System.out.println("The audience is taking their seats.");
    System.out.println("The audience is turning off their cellphones");
    try {
      jp.proceed();
      System.out.println("CLAP CLAP CLAP CLAP CLAP");
    } catch (Throwable throwable) {
      System.out.println("Boo! We want our money back!");    
    }
  }
  public void takeSeats() {
    System.out.println("The audience is taking their seats.");
  }
  public void turnOffCellPhones() {
    System.out.println("The audience is turning off their cellphones");
  }
  public void applaud() {
    System.out.println("CLAP CLAP CLAP CLAP CLAP");
  }
  public void demandRefund() {
    System.out.println("Boo! We want our money back!");
  }
}



2.1、经典的基于代理的AOP

创建通知
 

public class AudienceAdvice implements MethodBeforeAdvice,
    AfterReturningAdvice, ThrowsAdvice {
 
  public AudienceAdvice() {}
 
  //前通知,在方法之前调用
  public void before(Method method, Object[] args, Object target)
      throws Throwable {
    audience.takeSeats();
    audience.turnOffCellPhones();
  }
 
  //返回后通知,在成功返回后执行
  public void afterReturning(Object rtn, Method method,
      Object[] args, Object target) throws Throwable {
    audience.applaud();
  }
 
  //抛出后通知,在抛出异常后执行
  public void afterThrowing(Throwable throwable) {
    audience.demandRefund();
  }
 
  public void afterThrowing(Method method, Object[] args, Object target,
      Throwable throwable) {
    audience.demandRefund();
  }
 
  // injected
  private Audience audience;
  public void setAudience(Audience audience) {
    this.audience = audience;
  }
}


创建周围通知
相当于前通知,返回后通知,抛出后通知的结合,在spring里,周围通知是由MethodInterceptor接口定义的。周围通知的好处是能以简洁的方式在一个方法里定义所有的通知。当然如果单独使用前通知或者后通知,周围通知的优势就减弱了。后通知只能够对被通知方法的返回值进行检查,不能修改,利用周围通知不仅可以检查,还可以修改返回值,让我们可以在把方法的返回值传递给调用者之前进行一些处理。
 

public class AudienceAroundAdvice implements MethodInterceptor {
  public Object invoke(MethodInvocation invocation) throws Throwable {
  
    try {
      //方法调用之前执行
      audience.takeSeats();
      audience.turnOffCellPhones();
  
      //调用目标方法
      Object returnValue = invocation.proceed();
  
      //在成功返回之后执行
      audience.applaud();
    
      return returnValue;
    } catch (PerformanceException throwable) {
      //在出现异常之后执行
      audience.demandRefund();
      
      throw throwable;
    }
  }
  
  // injected
  private Audience audience;
  public void setAudience(Audience audience) {
    this.audience = audience;
  }
}


定义切点与通知者
声明正则表达式切点


    

把定义的切点与通知关联


     
     
 

 
联合切点与通知


  
  


定义AspectJ切点
 


   
   
 


创建代理


  
  
  

这里我们需要把实际的singer Bean 重命名为singerTarget,而且需要为所有代理的bean XML。


Spring自动代理被通知的Bean:

 
在spring AOP里,通知者把通知和切点关联起来,从而完整地定义了一个切面。但是切面在spring里是以代理的方式实现的。好在Spring提供了BeanPostProcessor的一个方便实现:DefaultAdvisorAutoProxyCreator,它会自动检查通知者的切点是否匹配Bean的方法,并且使用通知的代理来替换这个Bean的定义。它自动用匹配的通知者代理Bean。为了使用DefaultAdvisorAutoProxyCreator,只需要在配置如下的Bean:

创建自动代理

2.2、基于@AspectJ 注解驱动切面的自动代理

把Audience注解为一个切面
@Aspect
public class Audience {
    public Audience() {}
    @Pointcut("execution(* *.perform(..))")
    public void performance(){}
     
    @Before("performance()")
    public void takeSeats() {
        System.out.println("The audience is taking their seats.");
    }
    @Before("performance()")
    public void turnOffCellPhones() {
        System.out.println("The audience is turning off their cellphones");
    }
    @AfterReturning("performance()")
    public void applaud() {
        System.out.println("CLAP CLAP CLAP CLAP CLAP");
    }
    @AfterThrowing("performance()")
    public void demandRefund() {
        System.out.println("Boo! We want our money back!");
    }
}

配置下面这个元素,这个元素会在spring上下文创建一个AnnotationAwareAspectJAutoProxyCreator,从而根据@Pointcut注解定义的切点来自动代理匹配Bean。

2.3、定义纯粹的POJO切面


    
 
    
      
      
      
      
      
    
  

自动的使用JDK动态代理或者CGLIB来为目标对象创建代理,不要自己去定义自己的AutoProxyCreator
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。

如果你希望强制使用CGLIB代理 

proxy-target-class="true">
...

Spring IOC 和 AOP


推荐阅读
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
author-avatar
手机用户2502901401
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有