Spring之——面向切面编程

什么是AOP

AOP的全称是Aspect-Orented Progamming,即面切面编程(也称面向方向编程)。它是面向对象(OOP)的一种补充,目前已经成为一种比较成熟的编程方式。
在传统的业务处理代码中,通常会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式达到代码的重用,但是如果要实现某个功能(如记录日中),同样的代码依然会分散到各个方法中。这样如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。父子关系的纵向重用。虽然AOP是一种新的编程思想,但却不是OOP的代替品,它只是OOP的延伸和补充。

在AOP思想中,类与切面的关系如图。
在这里插入图片描述
从上图可以看出,通过Aspect(切面)分别在Class1和Class2的方法中加入了事务、日志、权限和异常功能。

Aop的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过对地关注其他业务逻辑实现,这不但提高了开发效率,而且增强了代码的可维护性。

目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理的方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,SpringAop引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了专门的编译器,在编译时横向代码织入。

AOP术语

通知、增强处理(Advice)

通知、增强处理(Advice) 就是你想要的功能,也就是上说的权限、事务、日志等。你给先定义好,然
后再想用的地方用一下。包含Aspect的一段处理代码。

连接点(JoinPoint)

连接点(JoinPoint) 这个就更好解释了,就是spring允许你是通知(Advice)的地方,那可就真多了,
基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接
点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法
有关的前前后后都是连接点。

切入点(Pointcut)

切入点(Pointcut) 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十
几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让
其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方
法,让切点来筛选连接点,选中那几个你想要的方法。

切面(Aspect)

切面(Aspect) 切面是通知和切入点的结合。现在发现了吧,没连接点什么事,链接点就是为了让你好
理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的
befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完
整的切面定义。

引入(introduction)

引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通
知定义的)用到目标类中吗

目标(target)

引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通
知定义的)用到目标类中吗

织入(weaving)

织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

使用Spring 实现AOP

写一个切入类

public class LogAspect {

    public void before(){
        System.out.println("执行方法前打印一条日志-----自定义形式");
    }

    public void after(){
        System.out.println("执行方法后打印一条日志-----自定义形式");
    }
    public void returning(){
        System.out.println("returning-----自定义形式");
    }
    public void throwing(){
        System.out.println("throwing-----自定义形式");
    }

}

在配置文件中配置如下内容:


    <bean name="logAspect" class="com.shen.aop.LogAspect"/>

    <aop:config>
        <aop:aspect ref="logAspect">
        <!--切入点 expression:表达式匹配要执行的方法-->
            <aop:pointcut id="pointcut" expression="(execution(* com.shen.service.impl.*.*(..)))"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut" />
            <aop:after-throwing method="throwing" pointcut-ref="pointcut"/>
            <aop:after-returning method="returning" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

上述配置代码片段中execution(* com.shen.service.impl..(…))就是定义的切入点表达式,该切入点表达式的意思是匹配com.shen.service.impl包中 任意类的任意方法执行。其中execution()是表达式的主体,第一个表示的是返回类型,使用表示所有类型;com.shen.service.impl表示的是需要拦截的包名,后面的2个表示的是类名,使用表示的是所有类;第三个表示的是方法名,使用表示所有方法;后面(. .)表示方法的参数其中“. .”表示任意参数。需要注意的是第1个*与包名之间有一个空格。

Spring AOP中切入点表达式的基本格式如下。

execution(modifiers-pattren?ret-type-pattern  declaring-type-pattern?name-pattern(param-pattern)  throws-pattern?)

上述格式中,各部分说明如下。

  • modifiers-pattren:表示定义的目标方法的访问修饰符,如public、private等。
  • ret-type-pattern:表示定义的目标方法的返回值类型,如void、String等。
  • declaring-type-pattern:表示定义的目标方法的类路径,如com.itheima.jdk.UserDaoImpl。
  • name-pattern:表示具体需要被代理的目标方法,如add()方法。
  • param-pattern:表示需要被代理的目标方法包含的参数。
  • throws-pattern:表示需要被代理的目标方法抛出的异常类型。

其中带有问号(?)的部分,如modifiers-pattern、declaring-type-pattern、throws-pattern表示可配置项,而其他部分表示必须配置项。

注解实现AOP

与基于代理类的AOP实现相比,基于XML声明式AspectJ要便捷的多,但它也存在着一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,Aspectj框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。
关于AspectJ注解的介绍,如表3-5所示

注解名称描述
@Aspect用于定义一个切面
@Printcut用于定义切入点表达式。
@Before前置通知,在连接点方法前调用
@After后置通知,在连接点方法后调用
@AfterReturningt返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
@AfterThrowingt异常通知,当连接点方法异常时调用

使用注解实现AOP:

@Aspect
public class LogAspect {
    @Before("execution(* com.shen.service.impl.*.*(..))")
    public void before( ){

        System.out.println("执行方法前打印一条日志-----自定义形式");
    }
    @After("execution(* com.shen.service.impl.*.*(..))")
    public void after(){
        System.out.println("执行方法后打印一条日志-----自定义形式");
    }
    @AfterReturning("execution(* com.shen.service.impl.*.*(..))")
    public void returning(){
        System.out.println("returning-----自定义形式");
    }
    @AfterThrowing("execution(* com.shen.service.impl.*.*(..))")
    public void throwing(){
        System.out.println("throwing-----自定义形式");
    }
 
}
@Service
public class User_uService implements IUserService {
    @Autowired
    IUser_uDao userUDao;

   
    @Override
    public User_u findUserById(Integer id) {
        System.out.println("方法在执行");

        return userUDao.findUserById(id);
    }
}

测试类:

public class Test {
    @org.junit.Test
    public void TestUser(){
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("application.xml");
        IUserService user =  applicationContext.getBean(IUserService.class);
        user.findUserById(1);
    }
}

执行结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

200Ok。。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值