Spring-02-AOP切面编程

Spring中的AOP的使用与JDK代理/cglib代理密切相关,与代理模式相关的内容的我的上篇博客Java设计模式–代理模式与JDK动态代理,cglib动态代理中有仔细的分析

一.AOP的概念

AOP即Aspect Oriented Programming,译为:面向切面编程,AOP技术是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是Spring框架中的一个重要内容,也是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率

二.AOP面向切面编程的目的及意义

AOP即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP通过封装、继承、多态等概念来建立起来的对象层次结构是一种纵向的关系。但是大型项目中有很多需要进行横向关系的抽象,比如日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术正是用来弥补OOP不足的一项技术,AOP对项目代码进行横向抽取,将多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面,在实际项目中的体现就是那些与业务无关,却为业务模块所共同调用的逻辑或责任。
通过AOP实现横向抽取后可以减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

三.Spring框架中AOP思想的应用

使用了AOP思想的Spring使得开发者能在不修改源码的情况下对程序进行功能增强。在Spring框架中,AOP切面编程主要用在事务处理,权限控制,日志记录,性能监控方面

2.1 AOP编程的相关概念:

Joinpoint(连接点)
所谓连接点是指那些被拦截到的点。在Spring框架中,连接点指的是方法,因为Spring只支持方法类型的连接点
Pointcut(切入点)
所谓切入点是指我们要对哪些Joinpoint进行拦截,目标类中所有的方法都是连接点,只有需要进行功能增强的方法是切入点,即切入点是连接点的子集
Advice(通知/增强)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知是切面要完成的功能,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知5种。通知就是对目标方法的功能扩展,又被称作增强
Introduction(引介)
引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field。
Target(目标对象)
代理对象的目标对象
Proxy(代理)
一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面)
是切入点和通知(引介)的结合
Weaving(织入)
目标对象进行功能增强并创建代理对象的过程。Spring采用动态代理(包括JDK代理和cglib代理)进行织入,而AspectJ采用编译期织入和类装载期织入

2.2 使用Spring框架模拟AOP式事务处理过程:

2.2.1 模拟过程需要三步:

1)编写业务组件
2)编写通知,即对业务组件的增强功能。同时在Spring配置文件中配置通知
3)定义切入点,一个切入点可能横切多个业务组件

2.2.2 模拟程序:

1)业务层,用来处理业务逻辑

public interface UserService {
    void save();
}
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户!");
    }
}

2)通知,通知的作用是对目标类进行增强

public class MyAdvice {
    //前置通知
    public void before(){
        System.out.println("这是前置通知!!");
    }
    //后置通知
    public void afterReturning(){
        System.out.println("这是后置通知(如果出现异常不会调用)!!");
    }
    //环绕通知,方法的参数是ProceedingJoinPoint,即连接点,在这里连接点是目标类
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("这是环绕通知之前的部分!!");
        //调用目标方法
        Object proceed = pjp.proceed();
        System.out.println("这是环绕通知之后的部分!!");
        return proceed;
    }
    //异常通知
    public void afterException(){
        System.out.println("出事啦!出现异常了!!");
    }
    //后置通知
    public void after(){
        System.out.println("这是后置通知(出现异常也会调用)!!");
    }
}

3)Spring配置文件,使用IOC和DI管理业务类,并配置切点和通知

<!-- 1.配置目标对象 -->
<bean name="userService" class="cn.spring.aop.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="cn.spring.aop.e_annotationaop.MyAdvice" ></bean>
<!-- 3.配置切入点及通知 -->
<aop:config>
        <!-- 配置切入点 -->
        <!-- 没有指定具体的方法,所以ServiceImpl类中所有的方法都是切入点,即对ServiceImpl类中所有的方法进行增强 -->
        <aop:pointcut expression="execution(* cn.spring.aop.service.*ServiceImpl.*(..))" id="pc"/>
        <!-- 配置通知 -->
        <aop:aspect ref="myAdvice" >
            <!-- 配置前置通知,一个名称为before的方法-->
            <aop:before method="before" pointcut-ref="pc" />
            <!-- 配置后置通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pc" />
            <!-- 配置环绕通知 -->
            <aop:around method="around" pointcut-ref="pc" />
            <!-- 配置异常通知 -->
            <aop:after-throwing method="afterException" pointcut-ref="pc"/>
            <!-- 配置后置通知 -->
            <aop:after method="after" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
</beans>

3)测试类

//用于测试的注解
@RunWith(SpringJUnit4ClassRunner.class)、
//加载Spring配置文件
@ContextConfiguration("classpath:applicationContext_3.xml")
public class AOPTest {
    //注入(DI)一个userService类的实例
    @Resource(name="userService")
    private UserService us;
    //测试test()方法
    @Test
    public void test(){
        us.save();
    }
}

2.3 AOP事务处理过程解析:

2.3.1 切入点配置(切入点表达式)

使用Spring框架进行AOP编程时,要在Spring配置文件中配置切入点表达式,切入点表达式用来和目标类中的方法进行匹配,匹配成功的方法为切入点,切入点表达式的写法如下:

1)匹配cn.spring.service包下的UserServiceImpl类的名称为save的方法,方法必须是是public共有方法,没有方法参数,且返回值为void

public void cn.spring.service.UserServiceImpl.save()

2)匹配cn.spring.service包下的UserServiceImpl类的名称为save的方法,方法没有方法参数,且返回值为void,可以为共有或私有

void cn.spring.service.UserServiceImpl.save()

3)匹配cn.spring.service包下的UserServiceImpl类的名称为save的方法,方法没有方法参数,且返回值可以为任意类型,可以为共有或私有

* cn.spring.service.UserServiceImpl.save()

4)匹配cn.spring.service包下的UserServiceImpl类的所有方法,方法没有方法参数,且返回值可以为任意类型,可以为共有或私有

* cn.spring.service.UserServiceImpl.*()

5)匹配cn.spring.service包下的名称中带有ServiceImpl的类(即可以是UserServiceImpl类,也可以是OrderServiceImpl类)的所有方法,方法参数任意,且返回值可以为任意类型,可以为共有或私有

* cn.spring.service.*ServiceImpl.*(..)

6)匹配cn.spring.service包及其子包下的名称中带有ServiceImpl的类的方法(即可以是UserServiceImpl,也可以是OrderServiceImpl),方法参数任意,且返回值可以为任意类型,可以为共有或私有

* cn.spring.service..*ServiceImpl.*(..)

2.3.2 Spring框架实现AOP编程的方式

Spring框架内部使用动态代理来实现AOP编程,其中动态代理由两种方式,JDK动态代理和cglib动态代理,使用哪种动态代理由Spring框架自动选择,也可以在Spring的配置文件中设置使用JDK动态代理或cglib动态代理,将标签<aop:config>中的属性“proxy-target-class”设置为true则会使用cglib动态代理,设置为false则会使用JDK动态代理。使用过程中一般不会设置此属性,由Spring自动识别并选择

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值