面向切面 AOP编程详解
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序的运行过程。
AOP底层,就是采用的动态代理模式实现的。采用了两种代理:JDK的动态代理,CGLib的动态代理
AOP的作用:利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用型和开发效率。
AOP编程有什么好处???
1、减少重复代码
2、专注业务开发
Tip:AOP只是对OOP的一种补充
AOP编程术语
1、切面(Aspect)
切面通常是交叉业务逻辑中的,横向开发,像日志、事务的处理,是对业务逻辑的增强
2、连接点(JoinPoint)
连接点是可以被植入具体业务方法的,这些方法都是连接点
3、切入点(Pointcut)
切入点指声明一个或多个连接点的集合
4、目标对象(Target)
就是将要被增强的对象
5、通知(Advice)
通知是定义了增强代码切入到目标代码的时间点,是放在目标对象执行前、后的
AOP常见的通知
@Before 前置通知 方法执行前调用 对应注解
@After 后置通知 方法执行后调用 对应注解
@AfterReturning 返回通知 方法返回后调用 对应注解
@AfterThrowing 异常通知 方法出现异常调用 对应注解
@Around 环绕通知 动态代理、手动推荐方法运行 对应注解
需要引入pom文件的jar包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
AspectJ的切入点表达式
AspectJ定义了专门的表达式,用于指定切入点,表达式原型为
execution([modifiers-pattern] // 访问权限类型
ret-type-pattern // 返回值类型
[declaring-type-pattern] // 全限定性类名
name-pattern(param-pattern) // 方法名(参数类型和参数个数)
[throws-pattern] // 抛出异常
)
/**
* 切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名
* 注意,表达式中加[]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
*/
例如,我们要给某个业务方法加上前置通知
1、准备一个Demo,定义一个切面类
Service层和实现层
package com.serivce;
public interface SomeService {
void doSome(String name, Integer age);
}
package com.serivce;
public class SomeServiceImpl implements SomeService {
public void doSome(String name,Integer age) {
System.out.println("doSome的的业务方法");
}
}
定义切面类
package com.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect // 声明这是一个切面类,做业务增强的
public class MyAspect {
// 定义一个方法,贴上@Before注解,设置需要前置通知的类
// 在这个方法中写入需要增强的功能,如日志,事务
@Before(value = "execution(* com.serivce.SomeServiceImpl.doSome(..))")
public void myBefore(JoinPoint jp){ // JoinPint jp 是连接点,做其他功能的,暂时忽略
System.out.println("前置通知:在目标方法之前,执行日志的功能");
// 连接点方法的定义
System.out.println("连接点方法的定义:"+jp.getSignature());
// 连接点方法的名称
System.out.println("连接点方法的名称:"+jp.getSignature().getName());
// 获取方法执行时的参数
Object args [] = jp.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
}
}
2、在applicationContext.xml中定义目标对象、切面类对象、自动代理生成器
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--声明目标类对象-->
<bean id="someService" class="com.serivce.SomeServiceImpl"/>
<!--声明切面类对象-->
<bean id="myaspect" class="com.aspect.MyAspect"/>
<!-- 声明自动代理生成器:使用aspectj把spring容器中目标类对象生成代理-->
<aop:aspectj-autoproxy/>
</beans>
3、测试
@Test
public void Test01(){
SomeService someService = (SomeService) ctx.getBean("someService");
someService.doSome("海百万",28);
}
}
结果如下