目录
1、动态代理
1.1、jdk动态代理
1.2、cglib动态代理
1.3、动态代理的好处
2、什么是AOP
2.1、AOP常用术语
2.2、切面的构成
3、使用aspectJ框架实现AOP
3.1、aspectJ简介
声明实现类ServiceImpl
声明切面
3.3、@AfterReturning后置通知
切面类代码
3.4、@Around环绕通知(功能最强的通知)
1、动态代理
1.1、jdk动态代理
使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。 jdk动态代理要求目标类必须实现接口,关于细节本文就不赘述了。
要求:
必须要有接口
目标类必须实现接口(一个或多个)
1.2、cglib动态代理
第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的,方法也不能是final的
1.3、动态代理的好处
在目标类源代码不改变的情况下,增加功能。
减少代码的重复
专注业务逻辑代码
解耦合,让你的业务功能和日志,事务非业务功能分离。
2、什么是AOP
面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了, 让开发人员用一种统一的方式,使用动态代理实现。
2.1、AOP常用术语
Aspect: 切面,给你的目标类增加的功能,就是切面。 像日志,事务都是切面。切面的特点: 一般都是非业务方法,独立使用的。
JoinPoint:连接点 ,连接业务方法和切面的位置。需要给哪个方法增加切面,这个方法就是连接点。
Pointcut : 切入点 ,指多个连接点方法的集合。
目标对象: 给哪个类的方法增加功能, 这个类就是目标对象。
Advice:通知,通知表示切面功能执行的时间。
2.2、切面的构成
切面就是要给别的方法进行增强的方法,一个切面有以下三个要素。
切面的功能代码,切面干什么
切面的执行位置,使用Pointcut表示切面执行的位置
切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
3、使用aspectJ框架实现AOP
3.1、aspectJ简介
aspectJ是一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。aspectJ框架实现aop有两种方式:
使用xml的配置文件 : 配置全局事务
使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
再使用aspectJ做aop之前要先加入aspectJ依赖。
<!--aspectJ依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.5.RELEASE</version></dependency>
3.2 @Before前置通知
前置通知注解修饰的切面在连接点方法之前执行。下面通过一段代码体验一下。
声明接口IService
public interface IService {void doSome(String name, int age);
}
声明实现类ServiceImpl
public class ServiceImpl implements IService {@Overridepublic void doSome(String name, int age) {System.out.println("===doSome()===");}
}
声明切面
@Aspect
public class MyAspectJ {/*** 定义功能增强方法(方法就是切面)* 1、方法的必须为public* 2、方法无返回值* 3、方法名称自定义* 4、方法可以有参数,也可以没有参数* 5、方法的定义上方加入注解,表示切入点的执行时机@Before(value = "execution(public void com.mms.ba01.ServiceImpl.doSome(String,int))")public void beforeLog() {System.out.println("前置通知->系统当前时间:" + new Date());}*//*前置通知,带方法参数的切面切面方法有参数时要求参数是JoinPoint类型,参数名自定义,该参数就代表了连接点方法,即doSome方法使用该参数可以获取切入点表达式、切入点方法签名、目标对象等*/@Before(value = "execution(* *..ServiceImpl.doSome(..))")public void beforeLog(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="+arg);}}
}
测试
public class MyTest {//aop前置通知@Testpublic void test01() {String config = "ba01/applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//获取目标对象,此时的service就是spring生成的代理对象//注意返回值类型是接口类型,不能是实现类接口,否则报错IService service = (IService) ac.getBean("service");//使用代理对象执行方法service.doSome("张三",23);}
}
3.3、@AfterReturning后置通知
在IService接口中新增方法:
Student doStudent(Student student);
在ServiceImpl实现doStudent方
@Overridepublic Student doStudent(Student student) {return student;}
切面类代码
@Aspect
public class MyAspectJ {/*** @AfterReturning: 后置通知,在连接点方法执行之后执行后置通知方法* 方法定义格式:* 1、公共方法* 2、没有返回值* 3、方法名称自定义* 4、与前置通知一样,可以有JoinPoint类型参数,该参数表示连接点方法对象;还可以有一个* Object类型参数,用于接收连接点方法的执行结果,注意该参数的参数名必须与切入点表达式* 的returning属性的属性值一致,表示将returning属性值赋给Object对象*//*@AfterReturning(value = "execution(* *..ServiceImpl.doOther(..))", returning = "obj")public void afterTransaction(JoinPoint jp, Object obj) {System.out.println("doOther方法的返回参数="+obj);System.out.println("事务已提交...");经过验证:在后置通知切面内不能改变连接点方法的返回值}*/@AfterReturning(value = "execution(* *..ServiceImpl.doStudent(..))", returning = "obj")public void afterTransaction(JoinPoint jp, Object obj) {System.out.println(obj);Student student = new Student();student.setName("李四");student.setAge(24);obj = student;System.out.println("===查看是否改变了连接点方法的返回值==="+obj);/*经过验证:在后置通知切面内不能改变连接点方法的返回值*/}
}
3.4、@Around环绕通知(功能最强的通知)
环绕通知是功能最强的通知,它的本质就是jdk动态代理,他可以在连接点方法之前和之后都可以执行,最厉害的是他可以改变连接点方法的执行结果(返回结果)。还是拿上面的doStudent(Student student)方法来说明,经过验证前置通知和后置通知都不能改变doStudent(Student student)方法的返回值。下面看一下环绕通知是如何做的。
切面类
@Aspect
public class MyAspectJ {/*环绕通知:@Around(切入点表达式)1、环绕通知是最重要的一个通知,他表示在连接点方法的前或者后都可以执行,它的本质就是jdk动态代理的invoke方法的method参数2、定义格式a、publicb、必须有返回值,类型为Object*/@Around(value = "pointCut()")/*再次回忆一下jdk动态代理的invoke方法的定义@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {是不是感觉与下面的方法定义眼熟啊,没错,环绕通知切面的定义实质上就是jdk动态代理*/public Object around(ProceedingJoinPoint pj) throws Throwable {System.out.println("环绕通知在连接点方法之前执行了...");Object result = null;result = pj.proceed();Student student = new Student();student.setName("李四");student.setAge(24);//改变连接点方法返回值result = student;System.out.println("事务已提交...");return result;}/*使用pointcut管理切面表达式1、在一个切面类中,若多个切面的切面表达式均为同一个,每次都要写重复的代码,此时就可以使用pointcut来管理切面表达式了2、定义格式:公共public无返回值无参数*/@Pointcut(value = "execution(* *.doStudent(..))")public void pointCut() {//空方法体}
}