本人是一名物联网工程专业大二的学生,是互联网浪潮中一朵小小的浪花,写博客即是为了记录自己的学习历程,又希望能够帮助到很多和自己一样处于起步阶段的萌新。临渊羡鱼,不如退而结网。与其羡慕别人有顶尖的技术,还不如自己从慢慢摸索学习。终有一天,你也能称为别人“羡慕”的人
博客主页:https://blog.csdn.net/qq_44895397
aop是一个规范,是动态化的一个规范,一个标准
AOP:
面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式
AOP就是动态代理的规范化,把动态代理的实现步骤,方法都定义好了,让开发人员用一种统一的方式,
使用动态代理。
怎么理解面向切面编程:
1)需要在分析项目功能时找出切面
2)合理的安排切面的执行时间
3)合理的安排切面执行的位置
术语:
1)Aspect:切面,表示增强功能,完成某一个个的功能,非业务功能
常见的切面功能有日志、事务、统计信息、参数检查、权限验证
2)JoinPoint:连接点,连接业务方法和切面的位置,就某类中的业务方法
3)Pointcut:切入点,指多个连接点方法的集合,多个方法
4)目标对象:哪个类要增加功能,这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间
切面的三个关键要素:
1)切面的功能代码,切面干什么的
2)切面的执行位置,使用Pointcut表示切面的执行位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后
aop的技术实现框架:
1、spring:spring在内部实现了aop规范能够做aop的工作
spring主要在事务处理时使用aop
但是spring的aop比较笨重
2、aspectJ:一个开源的专门做aop的框架,spring框架集成了aspectJ框架,通过spring就能够使用aspectj的功能
实现方式有两种:
1、使用xml的配置文件:配置全局事务
2、使用注解,项目中使用aop一般都使用注解,aspectj有5个注解
切入点表达式:
execution表达式中明显就是方法的签名,黑色字体部分可省略,各部分用空格分开,可以用到一下符号:
实现步骤:
1、增加maven依赖
1)spring依赖
2)aspectj的依赖
3)单元测试依赖
<!--添加spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--aspectj依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、创建,目标类:接口和他的实现类
给类中的方法增加功能
3、创建切面类:普通的类
1)在类的上面加入@Aspectj
2)在类中定义方法,方法就是切面要执行的功能代码,
在方法上添加aspectj中的通知注解(@Before)
需要指定切入点表达式,execution()
4、创建配置文件:声明对象,把对象交给容器统一管理,
声明对象你可以使用注解或者xml文件配置文件
- 声明目标对象
- 声明切面类对象
- 声明aspectj框架中的自动代理生成器标签
自动代理生成器,用来完成代理对象的自动创建功能的
5、创建测试类,从spring容器中获取目标对象(实际是代理对象)
通过代理方法,实现aop的功能增强
代码:
xml文件:
<!--创建实现类对象-->
<bean id="someServiceImpl" class="com.aspectj.domain.SomeServiceImpl"/>
<!--创建切面类的对象-->
<bean id="utilsTools" class="com.aspectj.domain.UtilsTools"/>
<!--
声明自动代理生成器,使用aspectj框架内部的功能,创建目标对象的代理对象
创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象
所以目标对象就是被修改后的代理对象
-->
<aop:aspectj-autoproxy />
切面类:
/**
* 切面类
* @Aspect :是aspectj框架中的注解
* 作用:表示当前的类是切面类
* 切面类:只用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:类定义的上面
*/
@Aspect
public class UtilsTools {
/**
* 定义方法,是实现切面功能的
* 方法定义的要求:
* 1、公共方法:public
* 2、方法没有返回值
* 3、方法名称自定义
* 4、方法可以有参数,也可以没有参数
* 如果有参数,参数不是自定义的,有几个参数类型可以使用
*/
/**
* @Before 前置通知注解
* 属性value,是切入点表达式,表示切面的功能执行的位置
* 位置:方法的上面
* public void doSome(String name,Integer age)
*/
@Before(value = "execution(public void com.aspectj.domain.SomeServiceImpl.doSome(String,Integer))")
public void dolog() {
//就是切面类要执行的功能代码
System.out.println("切面类" + new Date());
}
}
JoinPoint:
指定通知方法中的参数
业务方法,要加入切面功能的业务方法,
**作用是:**可以在通知方法中获取方法执行时的信息,例如方法名,方法参数
如果你的方法中需要用到方法的信息,就加入JoinPoint
这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数;
使用方法:
/**
* JoinPoint :切入点
* 可以获取方法执行的信息
*/
public void dolog(JoinPoint jp){
Object[] args = jp.getArgs();
for(Object arg:args){
System.out.println("参数"+arg);
}
System.out.println("切面"+new Date());
}
后置通知:@AfterReturning
属性:value:切入点表达式
return:自定义的变量,表示目标方法的返回值的
自定义的变量名必须和通知方法的形参名一样
位置:在方法定义的上面
特点:
1、在目标方法之后执行的
2、能够获取到方法的返回值,可以根据这个返回值的不同做不同的处理
3、可以修改这个返回值
具体使用:
@AfterReturning(value = "execution(* *..SomeServiceImpl.*(..))",
returning = "o")
public void doStudent(JoinPoint jp,Student o){
System.out.println("事务提交");
o.setName("多级包");
o.setAge(12);
}
环绕通知:@Around
定义格式
1、public
2、必须有一个返回值
3、方法名称自定义自定义
4、方法有参数,固定的参数,ProceedingJoinPoint
属性:value,切入点表达式
位置:在方法的定义上
特点:
1、他是功能最强的通知
2、在目标前后都能增强功能
3、控制目标方法是是否被调用执行
4、修改原来的目标方法的执行结果,影响最后的调用结果
环绕通知:
等同于jdk动态代理的,InvocationHandler接口
参数:ProceedingJoinPoint pjp 等同于Method
作用:执行目标方法的 pjp.doSome();
返回值:就是目标方法的执行结果,可以被修改
经常做的是事务
/**
* 环绕通知:功能最强大,能在业务方法前后增强功能
* value:切片表达式
* ProceedingJoinPoint:参数用来执行方法
*/
@Around(value = "execution(* *..SomeServiceImpl.*(..))")
public Object MyAspectj(ProceedingJoinPoint pjp) throws Throwable {
//控制方法是否执行
Object[] args = pjp.getArgs();
String name = "";
if (args != null && args.length > 1) {
name = (String) args[0];
}
Object proceed = null;
System.out.println("方法后执行");
//目标方法的调用
if ("yky".equals(name)) {
proceed = pjp.proceed();//相当于,method.invoke(),可以在这个方法前后增加功能
}
System.out.println("方法前执行");
//修改方法的返回值
if(proceed != null){
proceed = "hello world";
}
return proceed;
}
异常通知:@AfterThrowing
1、public
2、没有返回值
3、方法名称自定义
4、方法可以有参数Exception,如果还有是JoinPoint
属性:
value:切入点表达式
throwing:表示目标方法抛出的异常对象,自定义变量
变量名必须和方法的参数名一样
特点:
- 在目标方法抛出异常时执行
- 可以做异常的监控程序,监控方法是不是有异常,如果有异常,可以发邮件,短信进行通知
最终通知:@After
1、public
2、没有返回值
3、方法名称自定义
4、没有参数,,如果有是JoinPoint
特点:总是会执行,在目标方法后执行的 做资源清除的
@Pointcut:
定义和管理切入点,如果项目中有多个切入点是重复的,可以复用的,可以使用这个注解
-
属性value:切入点表达式
-
位置:自定义的方法上面
-
特点:
当使用@pointcut定义在一个方法的上面,此时方法的名称就是,切入点表达式的别名 其他通知中value属性可以使用这个方法名,代替切入点表达式了
代码:
@Before(value = "mycut()")
public void before(){
System.out.println("前置通知");
}
@After(value = "mycut()")
public void finalDemo(){
System.out.println("最终通知");
}
/**
* @Pointcut: 用来管理切入点
*/
@Pointcut(value = "execution(* *..SomeServiceImpl.*(..))\")")
public void mycut(){}
jdk动态代理实现:
1、创建目标类,SomeServiceImpl目标类,给他的doSome,doOther增加输出时间,事务
2、创建InvocationHandler接口的实现类,在这个实现类给目标方法增加功能
3、使用jdk中类Proxy,创建代理对象。实现创建对象的能力
public static void main(String[] args) {
//创建目标对象
SomeService someService = new SomeServiceImpl();
//创建
MyInvocationHandler handler = new MyInvocationHandler(someService);
创建代理
SomeService poxy = (SomeService) Proxy.newProxyInstance(someService.getClass().getClassLoader(),
someService.getClass().getInterfaces(),
handler);
poxy.dosome();
}
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 {
//通过代理对象执行方法时调用执行invoke()
Object res = null;
utilsTools.dolog();
//执行目标类的方法,通过Method类实现
res = method.invoke(target, args);//实现类的方法
utilsTools.doTrans();
//执行结果
return res;
}
}
本站所有文章均为原创,欢迎转载,请注明文章出处:爱敲代码的小游子