Spring之AOP使用

1.1 AOP概念基础

1.1.1 定义:

spring AOP是面向切面编程,主要思想是,将代码中的与主业务逻辑无关的公共代码,抽离出来,单独模块化为类即切面,在运行的时候动态的将切面的功能即通知加入到业务执行逻辑中。AOP模块常用于日志处理事务管理权限验证参数验证等。(前面统一异常处理,拦截器就是典型的AOP思想)

优点:

–每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级

–业务模块更简洁, 只包含核心业务代码.

 

1.1.2 Aop中的主要概念:

切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象,即共通处理的组件

 

•目标(Target):   被通知的对象

 

通知(Advice):  切面必须要完成的工作,用于指定切面和目标组件作用的时机。例如切面功能在目标方法之前或者之后执行等时机。

Spring框架提供五种类型的通知:

前置通知:先执行方面功能在执行目标功能。

后置通知:先执行目标功能再执行方面功能(正常异常都会执行)

返回通知:在方法返回结果之后执行(异常之后就不执行)

异常通知:先执行目标,抛出后执行方面

环绕通知:限制性方面前置部分,然后执行目标,最后再执行方面后置部分。

 

•代理(Proxy):    向目标对象应用通知之后创建的对象

 

•连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。

 

点(pointcut):每个类都拥有多个连接点是用于指定哪些组件和方法使用切面功能,在spring中利用一个表达式指定切入目标。

Spring常用的切入点表达式:

方法限定表达式:

execution(修饰符?返回类型 方法名(参数) throws 异常类型)

类型限定表达式:

within(包名.类型)

bean名称限定表达式

Bean(beanid或者name的属性值)

 

1.2 Spring AOP实现原理:

Spring AOP实现主要是基于动态代理技术,当spring采用aop配置后,spring容器返回目标对象,实质上是spring利用动态代理技术生成一个代理类型,代理类型重写了原目标组件方法的功能,在代理类中调用方面对象功能和目标对象的功能。

Spring框架采用了两种动态代理实现:

利用cglib工具包 (可以代理一切类)

目标没有接口时采用此方法。代理类是利用继承方法生成一个目标子类。

利用jdk proxy api (局限性,但效率高)

目标有接口时采用此方法,代理类是采用实现目标接口方法生成一个类。

 

代理的目的:是创建一个可以代理目标类的代理类,该代理类要重写目标类的方法。

动态代理:在程序运行阶段给要代理的接口/类生成实现类/子类 生成类存在于内存中,不存在硬盘上,在运行阶段需要创建类,再编译执行,因此效率低,但灵活度高。

 

1.3 开发实现形式:

创建方面组:

创建一个类,充当方面组件,实现通用业务逻辑。

声明方面组件:

applicationContext.xml中,声明方面组件

使用方面组件:

applicationContext.xml中,将方面组件作用到目标组件的方法上,并设置通知类型以确认方面组件调用的时机。

 

1.3.1 基于XML来配置AOP

需求:记录查询的操作日志。

 

1.3.1.1 导入相关的jar

org.springframework.aop-3.1.1.RELEASE.jar ---- ----spring 的面向切面编程,提供AOP(面向切面编程)实现
org.springframework.aspects-3.1.1.RELEASE.jar ---- ----spring 提供对 AspectJ 框架的整合

aopalliance.jar

aspectjweaver-1.7.4.jar

aspectjrt-1.7.4.jar

 

1.3.1.2 编写方面组件

public class Logger {

public void log(){

System.out.println("用户操作的信息");

}

 

}

 

1.3.1.3 声明方面组件

<!--     声明方面组件 -->

<bean id="logs" class="com.eduask.it.controller.Logger"></bean>

 

1.3.1.4 将方面组件作用到目标组件

此处用了前置通知作用到目标上,如果要使用返回通知后置通知则将before改为,after-returningafter

<!-- 将组件运用到目标组件上 -->

<aop:config>

<aop:aspect ref="logs">

<aop:before method="log" pointcut="within(com.eduask.it.controller..*)"/>

</aop:aspect>

</aop:config>

 

1.3.1.5 编写测试用例

/**

 * 测试aop运用到项目里面

 */

@Test

public void testAOP(){

ApplicationContext a = new ClassPathXmlApplicationContext("resource/applicationContext.xml");

UserController mapper = a.getBean(UserController.class);

 mapper.findUserList();

}

 

测试结果:输出结果将比目标结果先打印。

 

 

1.3.1.6 异常通知的案例演示

 

编写方面组件的具体方法

public void exLog(Exception e){

System.out.println("操作异常了"+e);

}

 

编写配置文件

<aop:aspect ref="logs">

<aop:after-throwing method="exLog" throwing="e" pointcut="within(com.eduask.it.controller..*)"/>

</aop:aspect>

注意:方法参数中的变量名要与配置文件throwing的属性值一样。否则不提示异常类型。

直接在上面的案例中执行。

 

 

1.3.1.7 环绕通知的案例演示:

 

编写方面组件的具体方法

import org.aspectj.lang.ProceedingJoinPoint;

/**

     * Proceedingjoinpoint 继承了 JoinPoint 。

     * 是在JoinPoint的基础上暴露出 proceed 这个方法。

     * proceed很重要,这个是aop代理链执行的方法。

     * 暴露出这个方法,就能支持 aop:around 这种切面

     */

public void opLog(ProceedingJoinPoint p) throws Throwable{

System.out.println("用户操作前");

p.proceed(); //环绕通知中执行目标的方法

System.out.println("用户操作后");

}

 

编写配置文件

<aop:aspect ref="logs">

<aop:around method="opLog" pointcut="within(com.eduask.it.controller..*)"/>

</aop:aspect>

 

然后测试,直接在上面的案例中执行。

 

1.3.2 基于注解来配置AOP

1.3.2.1 编写方面组件

 

@Component

@Aspect

public class CalculatorLogging {

@Before("within(com.eduask.it.controller..*)")

public void beforLogging(){

System.out.println("用户操作了。。。");

}

}

说明
@Aspect注解表示这个类作为一个切面
@Component注解表示这个类同样也要放在IOC容器中

 

@Before注解表示这个方法是用来作为前置通知,也就是在它签名中所标识的具体方法调用之前就会进入这个类
("within(com.eduask.it.controller..*)")

如果要使用后置通知,和返回通知则使用

@After注解表示这个方法是用来作为后置通知,也就是在它签名中所标识的具体方法调用之后才会进入这个方法

@AfterReturning注解表示这个方法是用来作为返回通知,也就是在它签名中所标识的具体方法调用并返回之后才会进入这个方法

若方法出现执行中出现异常, 则不会进入到返回通知

 

1.3.2.2 开启aop注解

<!-- 开启aop注解扫描 -->

<aop:aspectj-autoproxy proxy-target-class="true"/>

 

测试

在基于xml配置的案例中测试

1.3.2.3 异常通知:

@Component

@Aspect

public class CalculatorLogging {

@AfterThrowing(value="within(com.eduask.it.controller..*)",throwing="ex")

public void beforLogging(Exception ex){

System.out.println("用户操作了。。。"+ex);

}

}

 

@AfterThrowing注解表示这个方法是用来作为异常通知,也就是在它签名中所标识的具体方法调用并出现异常之后才会进入这个方法
并且要在方法参数里面添加一个"Exception ex",这个变量名要与刚才throwing所匹配的名字一致!
value属性表示所装配的类和方法
throwing表示返回的异常对象

1.3.2.4 环绕通知:

@Component

@Aspect

public class CalculatorLogging {

@Around(value="within(com.eduask.it.controller..*)")

public Object beforLogging(ProceedingJoinPoint jp) throws Throwable{

System.out.println("用户操作了。。。");

Object proceed = jp.proceed();

System.out.println("ddddd");

return proceed;

}

}

 

@Around注解表示这个方法是用来作为环绕通知,也就是在它签名中所标识的具体方法调用会进入这个方法
环绕通知其实就相当于一个代理,可以在里面写上前置、后置、异常或返回等

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值