Spring框架从入门到入土(四):AOP面向切面编程详解
动态代理复习
动态代理是指,程序在整个运行的过程中根本不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(不是真实定义的类)在程序运行时由JVM根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。
动态代理实现业务新增功能
原来的测试项目:
//接口
package com.liar.service;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:12
*/
public interface SomeService {
void doSome();
void doOther();
}
//接口实现
package com.liar.service.impl;
import com.liar.Utils.ServiceTools;
import com.liar.service.SomeService;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:14
*/
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
ServiceTools.doLog();
System.out.println("执行了someserviceimpl的dosome方法");
ServiceTools.doTrans();
}
@Override
public void doOther() {
ServiceTools.doLog();
System.out.println("执行了someserviceimpl的doother方法");
ServiceTools.doTrans();
}
}
//工具类
package com.liar.Utils;
import java.util.Date;
/**
* @author liar
* @date 编写时间: 2022/3/27 16:49
*/
public class ServiceTools {
public static void doLog(){
System.out.println("非业务方法的执行时间:"+ new Date());
}
public static void doTrans(){
System.out.println("非业务方法的执行完毕,提交事务!");
}
}
//主测试
package com.liar;
import com.liar.service.SomeService;
import com.liar.service.impl.SomeServiceImpl;
/**
* @author liar
* @date 编写时间: 2022/3/27 16:47
*/
public class MyApp {
public static void main(String[] args) {
//调用doSome doOther
SomeService service = new SomeServiceImpl();
service.doSome();
System.out.println("-------------------------");
service.doOther();
}
}
测试项目结构:
改造实现步骤:
- 创建目标类,SomeServiceImpl目标类,给它的方法增加输出时间和事务
- 创建InvocationHandler接口的实现类,这个类给目标方法增加功能
- 使用jdk类中proxy,创建代理对象,实现创建对象的能力
//创建InvocationHandler接口的实现类,这个类给目标方法增加功能
package com.liar.handler;
import com.liar.Utils.ServiceTools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author liar
* @date 编写时间: 2022/3/27 19:37
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行MyInvocationHandler中的invoke方法()");
//通过代理对象执行方法,会调用这个invoke()
Object res = null;
ServiceTools.doLog();
//执行目标类方法,通过method类实现
res = method.invoke(target, args);
ServiceTools.doTrans();
return null;
}
}
//使用jdk类中proxy,创建代理对象,实现创建对象的能力
package com.liar;
import com.liar.handler.MyInvocationHandler;
import com.liar.service.SomeService;
import com.liar.service.impl.SomeServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @author liar
* @date 编写时间: 2022/3/27 16:47
*/
public class MyApp {
public static void main(String[] args) {
//使用jdk的Proxy代理对象
//创建目标对象
SomeService target = new SomeServiceImpl();
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);
//使用Proxy代理
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),handler);
//通过代理执行方法
proxy.doSome();
}
}
- 执行结果
这种动态代理没有统一的标准,有时候会出现问题!
动态代理的作用
- 在目标类源代码不改变的情况下,增加功能
- 减少代码的重复
- 专注于业务逻辑代码
- 解耦合,让你的业务功能和日志,事务非业务功能分离。
AOP(Aspect Orient Programming)面向切面编程
简介:
Aop面向切面编程,基于动态代理,可以使用jdk,cglib两种代理方式
其实Aop就是一个动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员选一种统一的方式,使用动态代理。
-
Aspect: 切面,给你的目标类增加的功能,就是切面。比如:日志,事务处理
切面的特点:一般都是非业务的方法,独立使用
-
Orient :面向
-
Programming:编程
怎样理解面向切面编程?
- 需要在分析项目功能时候,找出切面
- 合理安排切面的执行时间,在目标方法之前还是目标方法之后
- 合理安排安全切面的位置,在哪个类,哪个方法增加增强功能
aop的技术实现框架:
-
spring:spring内部实现了aop规范,能做aop的工作
spring主要在事务处理使用aop
一般不会使用spring的aop实现,因为比较笨重
-
aspectj:一个开源的专门做aop的框架,spring框架继承了aspectj框架,通过spring框架就可以使用aspectj的功能。
- 使用xml配置文件实现:配置全局事务
- 使用注解实现
Aspectj的相关术语
-
Aspect:切面,表示增强功能,完成某一个功能,非业务功能。
常见的切面功能有:日志,事务,统计信息,参数检查,权限验证
-
JoinPoint:连接点,连接任务业务和切面的位置,就某类中的业务方法
-
Pointcut:切入点,指多个连接点的方法集合,多个方法
-
目标对象:给哪个类增加的功能,这个类就是目标对象
-
Advice:通知,通知表示切面功能的执行时间
一个切面有三个关键的要素:
- 切面的功能代码,就是切面要干什么?
- 切面执行位置,使用Pointcut表示
- 切面的执行时间,用Advice表示,在目标方法之前,还是在目标方法之后
Aspectj的切入点表达式
Aspectj定义了专门的表达式用于切入点。表达式的原型是
excution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern)
-
modifiers-pattern:访问权限类型
-
ret-type-pattern :返回值类型
-
declaring-type-pattern:包名类名
-
name-pattern(param-pattern):方法名(参数类型和参数个数)
-
throws-pattern 抛出异常类型
?表示可选部分
excution(访问权限类型 方法返回值类型 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名,所以excution表达式中明显就是方法的签名。注意,没有加粗部分可省略,各部分用;隔开。在其中可以使用以下符号:
符号 | 意义 |
---|---|
* | 0到多个任意字符 |
… | 用在多个方法参数中,表示多个参数用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类和子类,用在接口后,表示当前接口和实现类 |
举例子:
使用Aspectj切入点表达式
-
新建maven项目
-
加入依赖(spring依赖和aspectj依赖…)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
- 创建目标类:接口和它的实现类 要做的是给类中的方法实现功能
//接口
package com.liar.aspectj01;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:12
*/
public interface SomeService {
/**
* 测试方法1
* @param name
* @param age
*/
void doSome(String name,Integer age);
}
//实现类
package com.liar.aspectj01;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:14
*/
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能
//在方法前增加一个方法的执行时间
System.out.println("执行了目标dosome方法");
System.out.println("我是"+name+","+"今年"+age+"岁"+"。我是全世界最靓的仔!");
}
- 创建切面类:普通类
-
在类的上面加上@Aspect
-
在类中定义方法,方法就是切面需要执行的功能代码
在方法上面加入aspectj中的通知注解,例如@Before,有需要指定切入点表达式excution()
package com.liar.aspectj01;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
/**
* @author liar
* @date 编写时间: 2022/3/28 16:10
* @Aspect: 是aspectj框架中的类
* 作用:表示当前类是切面类
* 切面类:用来给方法增加功能的类,在这个类中有切面功能的代码
* 使用位置:在类中定义的上面
*/
@Aspect
public class MyAspect {
/*
* 定义方法,方法是实现切面功能的
* 方法的定义要求:
* 1. 公共方法 public
* 2. 方法没有返回值
* 3. 方法名称自定义
* 4. 方法可以有参数,也可以没有参数
* 如果有参数,参数不是自定义的,有几个参数类型可以使用
*/
/**
* @Before: 前置通知注解
* 属性:value,是切入点的表达式,表示切面的功能执行位置
* 位置:在方法上面
* 特点:
* 1. 目标方法之前先执行
* 2. 不会改变目标方法的执行结果
* 3. 不会影响目标方法的执行
*/
@Before(value = "execution(public void com.liar.aspectj01.SomeServiceImpl.doSome(String,Integer))")
public void myBefore(){
//就是切面要执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出时间:"+new Date());
}
}
- 创建spring的配置文件:声明对象,把对象交给容器统一管理,声明对象可以使用注解或者xml文件
-
声明标定对象
-
声明切面对象
-
声明aspectj框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能。
<?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">
<!--把对象交给spring容器创建由容器统一管理,创建对象-->
<!--声明目标对象-->
<bean id="someService" class="com.liar.aspectj01.SomeServiceImpl"/>
<!--声明切面类对象-->
<bean id="myAspect" class="com.liar.aspectj01.MyAspect"/>
<!--声明自动代理生成器:
使用aspectj框架内部功能,创建目标对象的代理对象
创建代理对象是在内存中实现的,修改目标对象的内存的结构,创建为代理对象
所以目标对象就是被修改后的代理对象
aspectj-autoproxy:会把容器中的所有的目标对象,一次性生成代理对象
-->
<aop:aspectj-autoproxy/>
</beans>
- 创建测试类,从spring容器中获取目标对象,实际上就是代理对象。
package com.liar;
import com.liar.aspectj01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author liar
* @date 编写时间: 2022/3/28 19:44
*/
public class MyTest01 {
@Test
public void test01(){
String config = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中取出对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标执行时增强了功能
proxy.doSome("李四",18);
}
}
切点表达式的多种写法
//原始
@Before(value = "execution(public void com.liar.aspectj01.SomeServiceImpl.doSome(String,Integer))")
//去掉public
@Before(value = "execution(void com.liar.aspectj01.SomeServiceImpl.doSome(String,Integer))")
//包路径用通配符表示
@Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
//返回值用通配符代替
@Before(value = "execution(* *..SomeServiceImpl.doSome(String,Integer))")
//通配符代替
@Before(value = "execution(* *..SomeServiceImpl.do*(..))")
//通配符代替
@Before(value = "execution(* do*(..))")
Aspectj基于注解的Aop实现
切面的执行时间,这个执行时间在规范中叫Advice(通知,增强)使用注解表示有:
- @Before
- @AfterReturning
- @Around
- @AfterThrowing
- @After
注解1@Before在上面的例子已有展示!!
JoinPoint参数
- 指定通知方法中的参数:JoinPoint
- JoinPoint:业务方法,要加入切面功能的业务方法
- 作用是:可以在通知方法中获取方法的执行时的信息,例如方法名称,方法的实参
- 如果你的切面功能中需要用到方法的信息,就加入JoinPoint
- 这个JoinPoint的值由框架赋予,必须是第一个位置的参数
/**
* 指定通知方法中的参数:JoinPoint
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用是:可以在通知方法中获取方法的执行时的信息,例如方法名称,方法事的实参
* 如果你的切面功能中需要用到方法的信息,就加入JoinPoint
* 这个JoinPoint的值由框架赋予,必须是第一个位置的参数
*/
@Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))")
public void myBefore(JoinPoint join){
//获取方法的完整定义
System.out.println("方法的签名(定义):"+join.getSignature());
System.out.println("方法的名称:"+join.getSignature().getName());
//获取方法的实参
Object[] args = join.getArgs();
for (Object obj :
args) {
System.out.println("方法的参数是:"+obj);
}
//就是切面要执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出时间:"+new Date());
}
}
- 测试结果
后置通知@AfterReturning
- 接口和实现类
/**
* 测试方法2
* @param name
* @param age
* @return
*/
String doOther(String name,Integer age);
/**
* 测试方法3
* @param name
* @param age
* @return
*/
Student doStudent(String name,Integer age);
@Override
public String doOther(String name, Integer age) {
System.out.println("执行了目标doOther方法");
//在方法之后增加一个功能
return "Hello aop!!";
}
@Override
public Student doStudent(String name, Integer age) {
System.out.println("执行了目标doStudent方法");
Student stu = new Student();
stu.setName(name);
stu.setAge(age);
return stu;
}
- 切面类
package com.liar.aspectj02;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
/**
* @author liar
* @date 编写时间: 2022/3/28 16:10
* @Aspect: 是aspectj框架中的类
* 作用:表示当前类是切面类
* 切面类:用来给方法增加功能的类,在这个类中有切面功能的代码
* 使用位置:在类中定义的上面
*/
@Aspect
public class MyAspect {
/*
* 后置通知的定义方法,方法是实现切面功能的
* 方法的定义要求:
* 1. 公共方法 public
* 2. 方法没有返回值
* 3. 方法名称自定义
* 4. 方法有参数,推荐是Object,参数名自定义
*/
/**
* @AfterReturning: 后置通知
* 属性:1. value 切入点表达式
* 2. returning 自定义的一个变量,表示目标方法的返回值
* 自定义的变量名和通知方法的形参名一样
* 位置:在方法定义的上面
* 特点:
* 1. 在目标方法执行之后
* 2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* Object res = doOther();
* 3. 可以修改这个返回值
*
* 后置通知的执行
* Object res = doOther();
* myAfterReturning(res);
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "obj")
public void myAfterReturning(Object obj){
//Object res是目标方法执行后的返回值,根据返回值做你的切面的功能处理
System.out.println("后置通知:在目标方法执行后,获得的返回值是:"+obj);
//修改目标方法的返回值,看一下是否会影响最后方法的调用结果
if(obj!=null){
obj = "Hello Aspectj!!!";
}
}
/**
* 当返回参数是一个对象
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doStudent(..))",returning = "obj")
public void myAfterReturning2(Object obj){
//Object res是目标方法执行后的返回值,根据返回值做你的切面的功能处理
System.out.println("后置通知:在目标方法执行后,获得的返回值是:"+obj);
//修改目标方法的返回值,看一下是否会影响最后方法的调用结果
//如果修改了res的内容,属性值等,是不是会影响后面的结果呢
if(obj!=null){
Student stu = (Student) obj;
stu.setName("小张");
stu.setAge(20);
}
}
}
- 配置文件和Student类省略,测试方法如下
@Test
public void test01(){
String config = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中取出对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标执行时增强了功能
String str = proxy.doOther("小梁",18);
System.out.println("结果是:"+str);
Student student = proxy.doStudent("小梁", 18);
System.out.println(student);
}
-
测试结果
如果是不可变的字符串,那么之后修改不会变化,如果是可变化的对象,会改变对象的值。
环绕通知@Around
- 接口和实现类
package com.liar.aspectj03;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:12
*/
public interface SomeService {
/**测试环绕通知
* @param name
* @param age
* @return
*/
String doFirst(String name,Integer age);
}
package com.liar.aspectj03;
/**
* @author liar
* @date 编写时间: 2022/3/22 15:14
*/
public class SomeServiceImpl implements SomeService {
@Override
public String doFirst(String name, Integer age) {
//在方法前增加一个方法的执行时间
System.out.println("执行了目标doFirst方法");
System.out.println("我是"+name+","+"今年"+age+"岁"+"。我是全世界最靓的仔!");
return "Hello Around";
}
}
- 切面类
package com.liar.aspectj03;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @author liar
* @date 编写时间: 2022/3/28 16:10
* @Aspect: 是aspectj框架中的类
* 作用:表示当前类是切面类
* 切面类:用来给方法增加功能的类,在这个类中有切面功能的代码
* 使用位置:在类中定义的上面
*/
@Aspect
public class MyAspect {
/**
* 环绕通知的定义格式
* 1. public
* 2. 必须有一个返回值,推荐使用Object
* 3. 方法名称自定义
* 4. 方法有参数,固定的参数ProceedingJoinPoint
*/
/**
* @Around :环绕通知
* 属性:value 切入点表达式
* 位置:在方法的定义上面
* 特点:
* 1. 他是功能最强的通知
* 2. 在目标方法的前和后都能增强功能
* 3, 控制目标方法的折执行结果,影响最后的调用结果
* @param point ProceedingJoinPoint等同于method
* 作用:执行目标方法
* @return 返回值:就是目标方法的执行结果,可以被修改
* 环绕通知相当于jdk动态代理,InvocationHandler接口
*
* 环绕通知经常做事务,在目标方法之前开启事务,执行目标方法,在方法之后提交事务
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint point) throws Throwable {
String name = "";
//获取第一个参数值
Object[] args = point.getArgs();
if(args != null && args.length > 1){
Object arg = args[0];
name = (String)arg;
}
//实现环绕通知
Object result = null;
//在目标方法之前加功能
System.out.println("环绕通知,在目标方法之前时间"+new Date());
//1. 目标方法的使用
if ("小梁".equals(name)) {
//符合条件调用目标方法
result = point.proceed();
}
//在目标方法之后加功能
System.out.println("环绕通知,在目标方法之后,提交事务");
//修改目标方法的执行结果,影响后面的调用结果
if(result != null){
result = "Hello MyLove!";
}
return result;
}
}
- 测试类
package com.liar;
import com.liar.aspectj03.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author liar
* @date 编写时间: 2022/3/28 19:44
*/
public class MyTest03 {
@Test
public void test01(){
String config = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中取出对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标执行时增强了功能
String str = proxy.doFirst("小梁",18);
System.out.println(str);
}
}
@AfterThrowing异常通知(了解)
- 接口和实现类
/**
* 测试异常通知
* @param name
* @param age
* @return
*/
String doSecond(String name,Integer age);
//实现接口
@Override
public String doSecond(String name, Integer age) {
System.out.println("执行了目标doSecond方法");
System.out.println("我是"+name+","+"今年"+age+"岁"+"。我是全世界最靓的仔!");
System.out.println(10/0);
return "Hello Around";
}
- 切面类
/**
* 异常方法的定义格式
* 1. public
* 2. 没有返回值
* 3. 方法名称自定义
* 4. 方法有一个参数Exception,如果还有就是JoinPoint
*/
/**
* @AfterThrowing :异常通知
* 属性:1. value 切入点表达式
* 2. throwing 自定义的变量,表示目标方法抛出的异常对象
* 变量名和方法的参数名一样
*
* 特点:1. 在目标方法抛出异常的时候执行
* 2. 可以做异常程序的监控,监控目标方法执行是不是有异常
* 如果有异常,可以发送邮件或者短信通知
*/
@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "e")
public void myAfterThrowing(Exception e){
System.out.println("异常通知:方法发生异常,执行:"+e.getMessage());
}
- 测试方法
@Test
public void test02(){
String config = "applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中取出对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标执行时增强了功能
String str = proxy.doSecond("小梁",18);
System.out.println(str);
}
- 测试结果
@After最终通知
无论方法是否抛出异常,该增强均会被执行。
其他测试代码省略,切面类如下:
/**
* 最终通知的定义格式
* 1. public
* 2. 没有返回值
* 3. 方法名称自定义
* 4. 方法没有参数,如果还有就是JoinPoint
*/
/**
* @After: 最终通知
* 属性:value 切入点表达式
* 位置:在方法的上面
* 特点:1. 总是执行
* 2. 在目标方法之后执行
*/
@After(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myAfter(){
//一般做资源清楚的工作
System.out.println("执行最终通知,总是会被执行的代码!");
}
Pointcut定义切入点
当较多的通知增强方法使用相同的execution切入点表达式时,编写、维护均较为麻烦。
AspectJ提供了@Pointcut注解,用于定义execution切入点表达式。
其用法是,将@Pointcut注解在一个方法之上,以后所有的execution的value属性值均
可使用该方法名作为切入点。代表的就是@Pointcut定义的切入点。这个使用@Pointcut注解
的方法一般使用 private的标识方法,即没有实际作用的方法。
原来的切面类中的方法
@Before(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public void myBefore(){
System.out.println("前置通知,在目标方法之前执行!");
}
@After(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public void myAfter(){
System.out.println("后置通知,在目标方法之后执行!");
}
改造之后
package com.liar.aspectj04;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Date;
/**
* @author liar
* @date 编写时间: 2022/3/28 16:10
* @Aspect: 是aspectj框架中的类
* 作用:表示当前类是切面类
* 切面类:用来给方法增加功能的类,在这个类中有切面功能的代码
* 使用位置:在类中定义的上面
*/
@Aspect
public class MyAspect {
@Before(value = "myPointcut()")
public void myBefore(){
System.out.println("前置通知,在目标方法之前执行!");
}
@After(value = "myPointcut()")
public void myAfter(){
System.out.println("后置通知,在目标方法之后执行!");
}
/**
* @Pointcut
* 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以进行复用的
* 可以使用Pointcut
* 属性:value 切入点表达式
* 位置:自定义的方法的上面
* 特点:
* 当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
* 其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式
*/
@Pointcut(value = "execution(* *..SomeServiceImpl.doFirst(..))")
private void myPointcut(){
//无需代码
}
}
最后补充:
- 没有接口的话使用的是cglib代理
- 如果期望有接口也使用cglib代理,需要在配置文件中设置
<!--
如果期望目标类有接口,使用clib代理
proxy-target-class="true"告诉框架,要使用cglib代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>