Spring面向切面编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring面向切面编程相关的知识,希望对你有一定的参考价值。

AOP面向切面编程,可以说是oop的完善补充,众所周知,面向对象编程特点是封装继承和多态,构建了一种对象层次结构,可以很好的组织代码,通过继承关系实现代码重用,但程序中总会有以一些重复的代码不太方便使用继承将他们重用和管理起来,这些代码的功能重复且需要用在不同的地方,虽然可以将它封装成公共函数,但在这种显示调用中并不是很方便。

AOP能够将重复的代码抽取出来单独维护,与设计公共子模块相比,公共子模块要调用除了直接硬调用外没有其他办法,而AOP处理这一类问题提供了灵活多样的方法。

AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP使用场景:

AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging  调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence  持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

AOP的相关一些概念:

方面(aspect):一个关注点的模块化,这个关注点的实现可能另外横切多个对象,比如事务管理。方面用Springadvidor或拦截器实现。

连接点(joinpoint):程序执行过程中明确的点,比如方法的调用或者特定的异常被抛出。

通知(advice):在特定的连接点,AOP框架执行的动作。

切入点(pointcut):指定一个通知将被引发的一系列连接点的集合。

引入(introduction):添加方法或字段到被通知的类。

目标对象(target object):包含连接点的对象,也称作被通知或被代理的对象。Pojo

Aop代理(aop proxy):aop框架创建的对象,包含通知,在spring中,aop代理通常是jdk动态代理或者cglib代理。

织入(weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

spring如何使用aop:

一、直接使用ProxyFactory来以编程方式使用spring aop,通过proxyFactory提供的方法可以设置target对象,最终通过getProxy方法获取代理对象。这里不做讲解。

二、配置XML文件,方式有四种:

1、配置ProxyFactoryBean,显式地设置advisors,advice, target

2、配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象

3、通过<aop:config>来配置

4、通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点

Xml通过<aop:config>实现示例:

依赖Spring-context会引入core beans context aoplliance aop commons-logging expression要使用aop还需要引入aspectjweaver包。

定义接口:

public interface You {
    voidbuyApple();
}

实现类:

public class YouImpl implements You {
    public void buyApple() {
      System.out.println("买苹果!");
    }
}

横切关注点:

public class Me {
    public void beatYou(){
      System.out.println("揍你!");
  }
  public void giveYouCandy(){
    System.out.println("给你糖!");
  }
}

配置文件:

<?xml version = "1.0" encoding= "UTF-8"?>
<beansxmlns="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.xsd
   http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
 
   <bean id="youImpl"class="com.gg.test.aop.YouImpl"></bean>
   <bean id="me"class="com.gg.test.aop.Me"></bean>
   
   <aop:config>
        <aop:aspect id="me"ref="me">
          <aop:pointcutid="addMethod" expression="execution(*com.gg.test.aop.You.*(..))" />
            <aop:before method="beatYou"pointcut-ref="addMethod" />
          <aop:after method="giveYouCandy"pointcut-ref="addMethod" />
      </aop:aspect>
   </aop:config>
</beans>

测试:

public class Test {
    public static void main(String[] args) {
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aopdemo.xml");
    You you = (You)applicationContext.getBean("youImpl");
      you.buyApple();
  }
}

结果:

技术分享

通过配置ProxyFactoryBean实现示例:

实现前置增强接口:

public class HijackBefore implementsMethodBeforeAdvice{
    public void before(Method method, Object[] args, Object target)throws Throwable {
      System.out.println("揍你!");
    }
}

配置文件:

<?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.xsd
   http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
 
   <bean id="youImpl"class="com.gg.test.aop.YouImpl"></bean>
   
   <!-- 定义劫持类的bean -->
   <bean id="hijackBeforeBean"class="com.gg.test.aop.HijackBefore" />
 
   <!--创建代理 -->
   <bean id="youProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
     <property name="target" ref="youImpl" />
     <property name="interceptorNames">
         <list>
             <value>hijackBeforeBean</value>
         </list>
     </property>
   </bean>
</beans>

测试:

public class Test {
    public static void main(String[] args) {
      ApplicationContext applicationContext = newClassPathXmlApplicationContext("aopdemo.xml");
        You youProxy = (You)applicationContext.getBean("youProxy");
      youProxy.buyApple();
  }
}

测试结果:

技术分享

通过advisor实现示例:

如果需求改为:你去买苹果。我在路上揍你,买香蕉时不揍你。

通知AdviceSpring提供的一种切面(Aspect)。但其功能过于简单,只能

将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。

顾问AdvisorSpring提供的另一种切面。其可以完成更为复杂的切面织入功能。PointcutAdvisor是顾问的一种,可以指定具体的切入点。顾问将通知进行了包装,会根据不同的通知类型,在不同的时间点,将切面织入到不同的切入点。

public interface You {
    void buyApple();
  void buyBunana();
}
public class YouImpl implements You {
    public void buyApple() {
      System.out.println("买苹果!");
  }
 
  public void buyBunana() {
    System.out.println("买香蕉!");
  }
}

实现前置增强接口:

public classHijackBefore implements MethodBeforeAdvice{
    public void before(Method method,Object[] args, Object target)throws Throwable {
  System.out.println("揍你!");
  }
}

配置文件:

<!--1.*:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问 -->
    <!-- 切面:顾问 顾问(Advisor)要包装通知(Advice)-->   
    <bean id="beforeAdvisor"class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice"ref="beforeAdvice"></property>
        <!--指定需要增强的方法:这里是buyApplet()方法,而buyBunana()方法则不会增强   -->
        <propertyname="mappedName" value="buyApple"></property>
        <!-- 也可以使用mappedNames指定多个方法
        <propertyname="mappedNames" value="doFirst,doSecond"></property>
        -->
    </bean>
       
    <!--*********************************************** -->
 
    <!-- 2.*:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问 -->
    <!-- 切面: 顾问 顾问(Advisor)要包装通知(Advice) -->
    <!-- <bean id="beforeAdvisor"
       class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice"ref="beforeAdvice"></property>
        <property name="pattern"value=".*doF.*t"></property>
        </bean> -->

测试:

public class Test{
    public static void main(String[] args){
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("aopdemo.xml");
      You youProxy =(You)applicationContext.getBean("youProxy");
      youProxy.buyApple();
      youProxy.buyBunana();
  }
}

测试结果:

技术分享

总结:

SpringAOP代理由SpringIOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。

Spring创建代理的规则为:

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB

AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

 


以上是关于Spring面向切面编程的主要内容,如果未能解决你的问题,请参考以下文章

Spring的AOP面向切面编程

spring入门面向切面编程

如何理解spring中的切面和过滤

Spring的AOP面向切面编程

Spring的AOP面向切面编程

Spring之面向切面编程指定切面执行顺序