Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。指的是所有可以被增强的方法。
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。指的是已经决定即将要增强的方法。比如我决定要对saveAccount方法进行增强了,那么该方法就是切入点。
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。增强的代码所在的方法。如果我们要用Logger类中的pringtLog方法对saveAccount()进行增强,那么printLog方法就是通知或者说是增强。
Introduction(引介):-了解
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。通知是方法层面的,而引介是针对类层面的,可以动态的添加一个类的属性或者方法。
Target(目标对象):
代理的目标对象。将要对哪个对象进行增强。
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。将通知应用到切入点的过程。
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。根据被代理对象的情况的不同,可能会生成一个基于接口的代理对象,也可能会生成一个基于子类的代理对象。
Aspect(切面):
是切入点和通知(引介)的结合。多个通知和多个切入的组合。
使用xml配置AOP:
<bean id="logger" class="com.demon.utils.Logger"/>
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<!-- 配置前置通知
method:配置通知方法(即具体进行增强的方法)
pointcut:配置AspectJ表达式,即将通知增强到哪个方法
execution:使用AspectJ的切入点表达式
execution(修饰符 返回值类型 包名.类名.方法名(参数列表))
-->
<aop:before method="beforePrintLog" pointcut="execution(public void com.demon.service.impl.AccountServiceImpl.saveAccount())"/>
<aop:after-returning method="afterReturningPrintLog" pointcut="execution(public void com.demon.service.impl.AccountServiceImpl.updateAccount(int))"
<aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(public void com.demon.service.impl.AccountServiceImpl.updateAccount(int)))"/
<aop:after method="afterPrintLog" pointcut="execution(public int com.demon.service.impl.AccountServiceImpl.deleteAccount())"/>
</aop:aspect>
</aop:config>
AspectJ切入点表达式说明
配置切入点表达式:aspectj表达式
1、修饰符可以省略:表示对所有方法进行增强
execution(void cn.itcast.service.impl.AccountServiceImpl.saveAccount())
2、返回值类型可以使用*代替:表示任意返回值类型
execution( * cn.itcast.service.impl.AccountServiceImpl.saveAccount())
3、包名可以使用*代替:表示任意名称的包名
execution( * *.*.*.*.AccountServiceImpl.saveAccount())
4、如果有多个包名,可以使用*..代替:表示任意多个包名
execution( * *..AccountServiceImpl.saveAccount())
5、类名可以使用*代替:表示任意名称的类
execution( * *..*.saveAccount())
6、方法名称可以使用*代替:表示任意名称的方法
execution( * *..*.*())
7、参数可以使用*代替:表示任意类型的参数,但至少有一个参数
execution( * *..*.*(*))
8、参数使用..代替:表示任意多个,任意类型的参数
execution( * *..*.*(..))
最终推荐的表达式的写法:
execution(* com.demon.service.impl.*.*(..))
使用注解配置AOP:
第一步:创建配置类
@Configuration
@ComponentScan(value = {"com.demon"})
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
第二步:注解装配service实现类
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Override
public void saveAccount() {
System.out.println("保存账户");
}
@Override
public void updateAccount(int i) {
System.out.println("更新账户");
}
@Override
public int deleteAccount() {
System.out.println("删除账户");
return 0;
}
}
第三步:在通知类上使用@Aspect注解声明为切面
@Component
@Aspect//表示当前类是一个切面类(也可以称之为通知类)
public class Logger {}
第四步:使用注解配置通知类型
配置前置通知、后置通知、异常通知、最终通知
@Component
@Aspect
public class Logger {
/**
* 指定切入点表达式
*/
@Pointcut("execution(* com.demon.service.impl.*.*(..))")
public void pt1() {
}
/**
* 前置通知
* value:用于指定切入点表达式,还可以指定切入点表达式的引用。
*/
@Before("pt1()")
public void beforePrintLog(){
System.out.println("前置通知执行了");
}
/**
* 后置通知
* value:用于指定切入点表达式,还可以指定切入点表达式的引用
*/
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("后置通知执行了");
}
/**
* 异常通知
* value:用于指定切入点表达式,还可以指定切入点表达式的引用
*/
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知执行了");
}
/**
* 最终通知
* value:用于指定切入点表达式,还可以指定切入点表达式的引用
*/
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知执行了");
}
}
配置环绕通知
/**
* 指定切入点表达式
*/
@Pointcut("execution(* com.demon.service.impl.*.*(..))")
public void pt1() {
}
/**
* 环绕通知
* value:用于指定切入点表达式,还可以指定切入点表达式的引用。
* spring框架为我们提供了一个接口,该接口可以作为环绕通知的方法参数来使用
* ProceedingJoinPoint。当环绕通知执行时,spring框架会为我们注入该接口的实现类。
* 它有一个方法proceed(),就相当于invoke,明确的业务层方法调用
* spring的环绕通知:
* 它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
@Around("pt1()")
public Object around(ProceedingJoinPoint pjp) {
Object proceed = null;
try {
beforePrintLog();
proceed = pjp.proceed();
afterReturningPrintLog();
} catch (Throwable throwable) {
throwable.printStackTrace();
afterThrowingPrintLog();
} finally {
afterPrintLog();
}
return proceed;
}
第五步:编写测试类
public class IAccountServiceTest {
private IAccountService accountService;
@Before
public void setUp() throws Exception {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
accountService = (IAccountService) ac.getBean("accountService");
}
@Test
public void saveAccount() {
accountService.saveAccount();
}
@Test
public void updateAccount() {
accountService.updateAccount(1);
}
@Test
public void deleteAccount() {
accountService.deleteAccount();
}
}
配置注解AOP的步骤
1、开启注解AOP
2、在切面类上使用@Aspect,声明这是一个切面类
3、在切面类中使用各类通知