AOP面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式
AOP就是动态代理的规范化,把动态代理的实现步骤方式都定义好了,让开发人员用一种统一的方式,使用动态代理
AOP(Aspect Orient Programming),面向切面编程,面向切面编程是从动态角度考虑程序运行过程。
AOP底层就是采用动态代理模式实现的,采用了两种代理:jdk的动态代理与cglib的动态代理。
怎么理解面向切面编程?
1.需要在分析项目功能时,找出切面。
2.合理安排切面的执行位置,(在目标方法前,还是目标方法后)
3.合理安排切面执行的位置,在哪个类,哪个方法增加增强功能。
术语:
1.Aspect:切面,表示增强的功能,非业务功能,常见的切面功能有日志,事务,信息统计,参数检查,权限验证。
aspectJ:一个开源的专门做aop的框架,spring框架中继承了aspectJ框架,通过spring就能使用aspectJ功能。aspectJ框架实现aop有两种方式:
1.使用xml的配置文件,配置全局事务
2.使用注解,在项目中要做aop功能,一般都使用注解
3.aspectJ有5个注解:
@Before前置通知,在目标方法执行前,执行增强功能
@AfterReturing后置通知,在目标方法执行后,执行增强功能
@Around环绕通知,在目标方法之前之后,执行增强功能
@Aft erThrowing异常通知,目标方法有异常,执行
@After最终通知,在目标方法执行后,执行,不管目标方法是否有异常抛出
AspectJ的切入点表达式
AspectJ定义了专门的表达式用于指定切入点,表达式原型:
execution(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?抛出异常类型
?表示可选部分
以上表达式公4个部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
使用aspectJ实现aop的基本步骤:
前置通知 @Before
1.新建maven项目
2.加入依赖,spring依赖,aspectJ依赖,junit单元测试
org.springframework
spring-context
5.2.5.RELEASE
org.springframework
spring-aspects
5.2.5.RELEASE
3.创建目标类,接口和他的实现类,要做的是给类中的方法增加功能
4.创建切面类,就是一个普通的类,但是创建时有要求
4.1.在类的上面加入@Aspect
4.2.在类中定义方法,方法就是切面要执行的功能代码,在方法的上面加上aspectJ中的通知注解,例如
前置通知@Before,有需要指定切入点表达式execution()
/**
* @Aspect:是spring框架中的注解
* 作用:表示当前类是切面类
* 切面类:用来给目标类增加功能
*
*/
@Aspect
public class MyAspect {
/*
* 方法的定义要求
* 1.必须是公共方法public
* 2.方法没有返回值void
* 3.方法名称自定义
* 4.方法可以有参数,参数不是自定义,有几个参数类型可以使用
* */
/*
* @Before:前置通知注解,目标类方法执行前,执行的功能,标志
* 属性value:是切入点的表达式,表示切面功能执行位置
* */
@Before(value = "execution(public void com.powernode.ba01.SomeServiceImpl.doSome(String,Integer))")
public void MyBefore(){
System.out.println("切面功能那个:输出方法执行时间"+new Date());
}
}
5.创建spring的配置文件:声明对象,把对象交给容器统一管理,声明对象可以使用注解或者xml配置文件
5.1.声明目标对象
5.2.声明切面对象
5.3.声明aspectJ框架中的自动代理生成器标签,用来完成代理对象的自动创建功能。
6.创建测试类,从spring容器中获取目标对象(实际就是代理对象),通过代理执行方法,实现aop的功能增强
public class MyTest01 {
@Test
public void test01(){
String ing = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
SomeService somServiec = (SomeService) ac.getBean("somServiec");
somServiec.doSome("李四",20);
}
}
------------------------------------------------------------------------------------------
后置通知
@AfterReturing
属性:1.value 切入点表达式
2.returning 自定义的变量,表示目标方法的返回值,自定义变量名必须和通知方法的形参名一样
位置:在方法定义的上面
特点:
1.在目标方法之后执行
2.能够获取到目标方法的返回值,可以根据这个返回者做不通的处理功能
3.可以修改这个返回值
实现步骤:
1.在接口中定义方法
2.在实现类,去实现这个方法
3.spring配置问价中指定类的位置
4.编写切面类
Object res可以获取到目标方法的返回值,我们可以对这个值,进行修改
JoinPoint jp作用:可以在通知方法中获取到目标方法执行时的信息,例如方法名,方法参数
如果切面功能需要用到目标方法的信息,就加入JoinPoint
这个JoinPoint参数值是由框架赋予,必须是第一位的参数
jp.getSignature()方法获取目标方法的返回值 包名类名方法名(参数类型)
package com.powernode.ba02;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Aspect:是spring框架中的注解
* 作用:表示当前类是切面类
* 切面类:用来给目标类增加功能
*
*/
@Aspect
public class MyAspect {
/*
* 方法的定义要求
* 1.必须是公共方法public
* 2.方法没有返回值void
* 3.方法名称自定义
* 4.方法有参数,推荐是Object
* */
/*
* 后置通知@AfterReturing
* */
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))",returning = "res")
/*
* returning = "res"就是doOther方法的返回值
* */
public void myAfterReturing2(JoinPoint jp,Object res){//res就是表达式,切入doOther()方法的返回值
System.out.println(jp.getSignature());
System.out.println("res---->"+res);
if (res != null){
Student student = (Student) res;
student.setName("马超");
student.setAge(28);
res= student;
}
System.out.println("后置通知,是在目标方法之后执行的,获取的返回值是:"+res);
}
}
5.创建测试类,测试
public class MyTest02 {
@Test
public void test01(){
String ing = "applicationContext.xml";
//容器对象创建,读取spring配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
SomeService somServiec = (SomeService) ac.getBean("somServiec");
String ss = somServiec.doOther("吕布", 30);
System.out.println(ss);
}
}
------------------------------------------------------------------------------------
增强方法有ProceedingJoinPoint参数
在目标方法执行之前之后执行,被注解为环绕增强的方法要有返回值,Object类型。并且方法可以包含一个ProceedingJoinPoint类型的参数。接口ProceedingJoinPoint其中一个proceed()方法,用于执行目标方法,若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回,该增强方法实际是拦截了目标方法的执行。
1.环绕通知方法定义格式
1.1.public
1.2.必须有一个返回值,推荐使用Object
1.3.方法名称自定义
1.4.方法有参数,固定的参数ProceedingJionPoint
实现步骤:
1.在接口中定义目标方法
2.实现类实现这个方法
//加入环绕通知环绕通知
@Override
public String doFirst(String name, Integer age) {
System.out.println("===doFirst()方法执行了===");
return "doFirst";
}
3.在切面类中增加方法
/**
* @Aspect:是spring框架中的注解
* 作用:表示当前类是切面类
* 切面类:用来给目标类增加功能
*
*/
@Aspect
public class MyAspect {
/*
* 方法的定义要求
* 1.必须是公共方法public
* 2.方法必须有一个返回值,推荐是public
* 3.方法名称自定义
* 4.方法有参数,推荐是ProceedingJionPoint
* */
/*
* Around 环绕通知
* 属性value:切入点表达式
* 特点:
* 1.他是最强的通知
* 2.在目标方法的前后增强功能
* 3.控制目标方法时候被调用
* 4.修改原来目标的执行方法的结果,影响最后的调用结果
* 环绕通知等同于jdk动态代理的中的,InvocationHandler接口
*
* 参数:ProceedingJoinPoint 就等同于Method,作用:执行目标方法
* 返回值:就是目标方法的执行结果,可以修改
*
* */
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name = "";
Object proceed = null;
//获取目标方法的参数 doFirst("孙策", 30);
Object[] args = pjp.getArgs();
if(args != null && args.length>1){
name= (String) args[0];
}
System.out.println("------------环绕通知");//前置环绕增强功能
if(name.equals("孙策")){
//执行目标方法
proceed = pjp.proceed();
}
System.out.println("============环绕通知");//后置环绕增强功能
//修改目标方法的结果
proceed = "ProceedingJoinPoint";
//返回目标方法执行结果
return proceed;
}
}
4.测试类,测试
public class MyTest03 {
@Test
public void test01(){
String ing = "applicationContext.xml";
//容器对象创建,读取spring配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(ing);
SomeService somServiec = (SomeService) ac.getBean("somServiec");
String ss = somServiec.doFirst("孙策", 30);
System.out.println(ss);
}
}
---------------------------------------------------------------------------------
@Aft erThrowing异常通知【了解】
---------------------------------------------------------------------------------------
@After最终通知【了解】
无论目标方法是否抛出异常,该功能均会被执行
--------------------------------------------------------------------------------
@Pointcut辅助注解【了解会用就行】
就是一个辅助注解,不是通知注解
aop:看做是动态代理的规范化,把实现动态代理的步骤进行了一个规范化定义
aop作用:
1.在目标类不修改源代码的情况下,增加功能
2.减少代码的重复
3.专注业务功能的实现
4.解耦合
什么时候考虑使用aop技术?
1.给类修改功能,但是没有源代码,使用aop技术
2.项目中多个类添加相同功能
3.给业务增加事务,日志输出
aop的实现框架?
spring实现了aop,实现方式时接口
aspectJ框架,使用注解可以实现aop,使用xml配置文件中的标签实现aop功能
aop中的概念
1.aspect:切面,表示给业务方法增加功能
2.pointcut:切入点,是一个或多个JopinPoint集合,表示切面功能执行的位置
3.advice:通知,也叫增强,表示切面执行的时间,在方法前,方法后等
aspectJ框架的使用
1.表示切面的执行时间,使用的通知注解,@Before:前置通知,在目标方法执行前,执行切面方法功能
2.表示切面位置的切入点表达式:execution(访问修饰符 返回值 包名.类名.方法名(参数类型) 异常类型)
目标类没有接口spring框架自动使用cglib,如果目标类有接口还想用cglib怎么实现?
在spring配置文件中加入
proxy-target-class="true":告诉框架,要使用cglib动态代理