动态代理:可以在程序的执行过程中,创建代理对象
通过代理对象执行方法,给目标类的方法增加额外的功能(功能增强)
1.动态代理实现方式:
jdk动态代理,使用jdk中的Proxy,Method,IovocaitonHanderl创建代理对象
cglib动态代理: 第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类,子类就是代理对象,要求目标类不能是final的,方法也不能是final的
2.动态代理的作用:
1.在目标类源代码不改变的情况下,增加功能
2.减少代码的重复
3.专注业务逻辑代码
4.解耦合,让你的业务功能和日志,事务非业务功能分离
3.Aop:面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式
Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理
4.Aop(Aspect Orient Programming):面向切面编程
5.Aop的实现
AOP是一个规范,是动态的一个规范化,一个标准
aop是奇数实现框架
1.spring: spring在内部实现了aop规范,能做aop的工作
spring主要在事务处理时使用aop
项目开发中很少使用spring的aop实现,因为很笨重
2.aspectJ: 一个开源的专门做aop的框架,spring框架中集成了aspectJ框架,通过spring就能使用aspectJ的功能
-
1.使用xml的配置文件:配置全局事务
-
2.使用注解,我们在项目中要做aop功能,一般使用注解,aspectJ有5个注解
1.切面的执行时间,这个执行时间的规范中叫做Advice(通知,增强)
在aspectJ框架中使用注解表示的,也可以使用xml配置文件中的标签
-
1.@Before *
-
-
2.@AfterReturning *
-
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res") public void myafterReturing(Object res){ //Object res:是目标方法执行后的返回值,根据返回值做切面的功能处理 System.out.println("后置通知,返回值是:"+res); } -
3.@Around *
-
@Aspect public class MyAspect { @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name =""; //获取第一个参数值 Object[] args = pjp.getArgs(); if (args!=null && args.length>1){ Object arg = args[0]; name = (String) arg; } System.out.println(name); //实现环绕通知 Object result = null; System.out.println("环绕通知:在目标方法之前,输出:"+new Date()); //目标方法的调用 if ("王五".equals(name)) { //名字为王五,符合条件值目标方法 result = pjp.proceed(); } System.out.println("==环绕通知.在目标方法之后,提交事务=="); if (result!=null){ result = "Hello AspectJ Aop"; } return result; } } -
4,AfterThrowing: 异常通知
-
5.@After: 最终通知
-
6.@Pointcut: 定义切入点
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-patern): 方法名(参数类型和参数个数)
throws-pattern: 抛出异常类型
?表示可选部分
以上表达式4个部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名,所以,execution表达式中明显就是方法的签名
其中可以使用以下符号
*: 0值多个任意字符
..: 用在方法参数中,表示任意多个参数用在包名后,表示包及其子包路径
+: 用在类名后,表示当前类及其子类,用在接口后,表示当前接口及其实现类
举例:
execution(public * *{..})
指定切入点为: 任意公共方法
execution(* set*(..))
指定切入点为: 任何一个以"set"开始的方法
execution(* com.xyz.service.*.*(..))
指定切入点为: 定义在service包里任意类的任意方法
execution(* com.xyz.service..*.*(..))
指定切入点为: 定义在service包或子包里的任意类的任意方法,".."出现在类名中时,后面必须跟" * ", 表示包,子包下的所有类
execution(* *..service.*.*(..))
指定所有包下的service子包下所有类(接口)中所有方法为切入点
指定通知方法中的参数: JoinPoint
JoinPoint: 业务方法,要加入切片功能的业务方法
作用是:可以在通知方法中,获取方法执行时的信息,例如方法名称,方法参数
如果你的切片功能中需要用到方法的信息,就加入JoinPoint
这个Joinpoint参数的值是由框架赋予的,必须是第一个参数
@Before("execution(* *..SomeServiceImpl.doSome(..))")
public void myBefore(JoinPoint jp){
//获取方法的完整定义
System.out.println("方法的签名(定义)"+jp.getSignature());
System.out.println("方法的名称"+jp.getSignature());
//获取方法的实参
Object[] args = jp.getArgs();
for (Object arg: args) {
System.out.println("参数:"+arg);
}
//切面要执行的功能代码
System.out.println("前置通知,切面功能:在目标方法之前输出时间"+new Date());
}
cglib代理



