1.jdk动态代理
package com.lxd.proxyjdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* DOTO
*
* @authorLLLL
* @since2022-07-06 19:24:47
*/
public class Test01 {
public static void main(String[] args) {
//创建目标对象
Ufunc ufunc=new UfuncImpl();
ClassLoader classLoader = Test01.class.getClassLoader();
Class[] interfaces=ufunc.getClass().getInterfaces();
/*
* 创建动态代理对象以下方法需要三个参数
* 1.ClassLoader
* ClassLoader是类加载器,顾名思义,就是用来加载类的字节码的。我们知道要创建
* 一个对象时需要调用类的构造器,调用构造器的时候,就会引起类的加载,加载类刚好
* 就是使用类加载器完成的而以下创建代理对象的代码,并没有调用构造器,这是一种不
* 太正常的创建方式,但是仍然需要类加载器加载类的字节码!所以必须给该方法传入一个
* 类加载器。
*
* 2.Class []
* 问题是,传入classloader类加载器要加载那个类的字节码呢?加载的字节码是运行时
* 自动生成的!不像常规做法,常规做法是先编写类的源代码,再编译源代码进而生成字节
* 码,动态代理要加载的字节码没有源码!!!这个字节码就是运行时动态生成的!!!
* 为题是,运行时生成的字节码,有什么根据?字节码中有哪些方法?如何确定呢?这刚好就是
* 第二个参数决定的!第二个参数是一个接口的字节码数组,最终生成的字节码就是一个实现了
* 指定接口的类的字节码
* 3.
* 问题是调用代理对象的方法时,代理对象的方法体在哪???这刚好就是第三个参数决定的
* 每次对代理对象的任何方法调用,都会进入第三个参数的 invoke方法中
*
* */
Ufunc u=(ufunc) Proxy.newProxyInstance(classLoader,interfaces,new
myHandler(ufunc));
}
}
class myHandler implements InvocationHandler{
private Object target;
public myHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始方法名字"+method.getName()+"参数"+ Arrays.toString(args));
//把method代表的方法当作target的方法调用 参数时args
Object o = method.invoke(target, args);
System.out.println("结束方法名字"+method.getName()+"结果"+ o);
return null;
}
}
2.aop快速起步
2.1引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
2.2创建切面类
2.3配置xml文件
javaconfig类代替xml文件
运行结果
3.aop四个通知
@Component
@Aspect
public class Aaa {
//一个通知就是一个方法该方法告诉你在什么时间什么地点要做什么事情
@Before("execution(* com.lxd.test.*.*(..))")
public void before(){
System.out.println("前置通知");
}
@After("execution(* com.lxd.test.*.*(..))")
public void After(){
System.out.println("后置通知");
}
@AfterThrowing("execution(* com.lxd.test.*.*(..))")
public void throwsAfter(){
System.out.println("异常通知");
}
@AfterReturning("execution(* com.lxd.test.*.*(..))")
public void returnning(){
System.out.println("返回通知");
}
}
异常通知和返回通知的参数获取
@AfterThrowing(value = "execution(* com.lxd.test.*.*(..))",throwing = "e")
public void throwsAfter(Exception e){
System.out.println("异常通知"+e);
}
@AfterReturning(value = "execution(* com.lxd.test.*.*(..))",returning = "r")
public void returnning(Object r){
System.out.println("返回通知"+r);
}
4.joinpoint的使用
//作为通知的方法,可以接受一个可选的参数:JoinPoint 如果写了这个参数
//该参数中封装了本次拦截的方法签名(public void before)
public void before(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();//获取方法参数
String name = joinPoint.getSignature().getName();//获取代理对象执行方法的方法名
System.out.println(name+":前置通知");
}
5.@Around环绕通知的使用
@Around("execution(* com.lxd.test.*.*(..))")
public Object around(ProceedingJoinPoint point){
try {
System.out.println("前置通知");
//这个方法就是目标方法的执行
// 等价于Object o = method.invoke(target, args);
Object o = point.proceed();
//以下return 会把返回值,返回到代理对象调用方法的调用处
System.out.println("返回通知");
return o;
}catch (Throwable e){
System.out.println("异常通知");
throw new RuntimeException(e);
}finally {
System.out.println("后置通知");
}
}
}
6.@Pointcut重用切点表达式
//重用切点表达式
@Pointcut("execution(* com.lxd.test.*.*(..))")
public void xxx(){
}
//通知告诉你在什么时间什么地点要做什么事情
@Before("xxx()")
//作为通知的方法,可以接受一个可选的参数:JoinPoint 如果写了这个参数
//该参数中封装了本次拦截的方法签名(public void before)
public void before(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();//获取代理对象执行方法的方法名
System.out.println(name+"方法"+":前置通知");
}
@After("xxx()")
public void After(){
System.out.println("后置通知");
}