文章目录
1. AspectJ定义了专门的表达式用于切入点,表达式的原型是
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
- modifiers-pattern 访问权限类型(public,protected,private,默认)
- ret-type-pattern 返回值类型 (void,简单类型,引用类型等)
- declaring-type-pattern 包名类型
- name-pattern(param-pattern) 方法名(参数类型和参数个数)
- throws-pattern 抛出异常类型
- ? 表示可选的部分
以上表达式共四个部分.
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名,所以,execution表达式
中明显就是方法的签名,注意,表达式中不是粗体的部分表示可省略部分
各部分间用空格分开,其中可以使用以下符号
符号 | 意义 |
* | 0至多个任意字符 |
.. | 用在方法参数中,表示任意多个参数 |
用在方法参数中,表示任意多个参数 | |
* | 用在类名后,表示当前类及其子类 |
用在接口后面,表示当前接口及其实现类 |
举例说明
//指定切入点为任意公共方法
execution(public * *(..))
//指定切入点为任意"set"开头的方法
ececution(* set*(..))
//指定切入点:service包中的任意类的任意方法
execution(* com.ezj.service.*.*(..))
//指定切入点:定义在service包或者其子包中的任意类中的任意方法
//".."出现在类名中时,后面必须跟"*",表示包,子包下的所有类
execution(* com.wzj.service..*.*(..))
//指定切入点:表示所有包在的service子包中的所有类中的所有方法
execution(* *..service.*.*(..))
2.目标对象和代理对象
目标对象:在SpringAOP被增强的对象
代理对象:通过aop中对目标对象进行增强,加入代理逻辑的而产生的对象
3.AspectJ在项目中的使用
@Aspect: 是aspect框架中的注解
作用:表示当前类是切面类
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
位置:在类的定义上面
@Aspect
public class MyAspect {
}
使用AspectJ的时候,要在配种文件中加入
<!-- 声明自动代理生成器:使用aspect框架内部的功能,创建目标对象的代理对象
创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象
所以目标对象就是被修改后的代理对象
aspectj-autoproxy:会把spring容器中的所有目标对象,一次性都生成代理对象
-->
<aop:aspectj-autoproxy/>
4.在AspectJ中击中常用注解的使用
1.@Before
- @Before() 前置注解通知
属性 value 是切入表达式,表示切入的功能执行的位置
位置 : 在方法的上面
特点:
1.在目标方法之前执行
2.不会改变目标方法的执行结构
3.不会影响目标方法的执行
/**前置通知方法方法,方法是实现切面功能的
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法名称自定义
4.方法可以有参数,也可以没有参数
如果有参数,参数不是自定义的*/
@Before(value = "execution(* *..doSome(String,Integer))")
public void MyBefore(JoinPoint jp){
//就是切面要执行的功能代码
System.out.println("1前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
2.JoinPoint
指定通知方法中的参数;JoinPoint
JoinPoint:业务方法,需加入切面功能的业务方法
作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参
如果你的切面功能中需要用到方法的信息,就加入JoinPoint
这个JoinPoint参数的值是由框架赋值,必须是第一个参数的位置
@Before(value = "execution(* *..doSome(String,Integer))")
public void MyBefore(JoinPoint jp){
//获取方法的完整定义
System.out.println("方法的签名(定义)="+jp.getSignature());
System.out.println("方法的名称="+jp.getSignature().getName());
//获取方法的实参
Object[] args = jp.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
//就是切面要执行的功能代码
System.out.println("1前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
3.@AfterReturning
@AfterReturning : 后置通知
属性: 1.value 切入点表达式
2.returning 自定义的变量,表示目标方法中的返回值
自定义方法的返回值,必须和后置通知方法的形参名一样
位置:在方法定义的上面
特点:
1.在目标方法之后执行的
2.能够获取到目标方法的返回值,可以根据这个放回值做不同的处理
Object res = doOther();
3.可以修改这个返回值,如果传入的是基本类型和String,修改后不影响程序的最终结果,如果是引用类型,修改后会改变最后输出的结果
/**
* 后置通知定义方法,方法是实现切面功能的
* 方法的定义要求:
* 1.公共方法 public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法可以有参数,
*/
@AfterReturning(value = "execution(* *..Some*.doOther2(String,Integer))",
returning = "res" )
public void myAfterReturning(Object res){
System.out.println("后置通知,在目标方法执行之后,获取的返回值式:"+res);
if(res != null){
Student student = (Student)res;
student.setAge(20);
//修改后会改变最后的输出结果
student.setName("jack");
}
}
目标类中的方法
@Override
public Student doOther2(String name, Integer age) {
Student student = new Student(name,age);
return student;
}
测试类
public class AppTest {
@Test
public void test01() {
//创建spring容器
ApplicationContext cs = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService someService = (SomeService) cs.getBean("someService");
Student student = someService.doOther2("tom", 2);
System.out.println(student);
}
}
运行结果
后置通知,在目标方法执行之后,获取的返回值式:Student{name='tom', age=2}
Student{name='jack', age=20}
4.@Around
@Around() 环绕通知
属性 value 是切入表达式,表示切入的功能
位置 : 在方法的上面
特点:
1.它是功能最强的通知
2.在目标方法的前后都能增强功能
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结构 影响最后的结
环绕通知,等同于jdk动态代理的invocat
参数 : ProceedingJoinPoin
作用:执行目标方法的返回值:就是目标方法的执行结果,可以被修改
/**
* 环绕通知方法的定义格式
* 方法的定义要求:
* 1.公共方法 public
* 2.方法必须有一个返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法可以有参数,固定的参数 ProceedingJoinPoint
*/
@Around(value = "execution(* *..doFirst(String,Integer))")
public Object MyAround(ProceedingJoinPoint pjp) throws Throwable {
//实现环绕通知
Object result = null;
System.out.println("环绕通知,在目标方法之前,输出时间:" + new Date());
//1.目标方法调用
//2.在目标方法前后都能添加功能
result = pjp.proceed();//method.invoke; Object result=doFirst
if ("ab".equals(result)) {
System.out.println("环绕通知,在目标方法之后,提交事务");
}
if(result != null){
//修改返回值,回直接影响最后的返回值
result = "hello aspect";
return result;
}
return result ;
}
}
5.@PointCut
@PointCut 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,
可以使用@PointCut
属性:value 切入点表达式
位置:在自定义的方法上面
特点:
当使用@PointCut定义在一个方法的上面,此时这个方法的名称就是切入点表达式
其他的通知中,value属性就可以使用这个方法,代表切入点表达式
@Before(value = "myPt()")
public void MyBefore(){
System.out.println("1前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
}
@After(value = "myPt()")
public void MyAfter(){
System.out.println("后置通知,在目标方法之后:");
}
@Pointcut(value = "execution(* *..doSome(String,Integer))")
public void myPt(){
//无需代码
}
5.@ After
最终通知,总是会被被执行的代码