动态代理
我们还未学习面向切面编程时,总是把某些方法在业务逻辑中调用,例如权限检查,记录日志等;这样做会导致后期维护时会有些混乱;因此我们需要采用AOP面向切面编程;将权限检查,记录日志等方法称为增强,并放在切面类中,再创建代理对象,对目标对象进行处理,使之附上之前约定的那些增强方法;
JDK动态代理
JDK动态代理的步骤如下:
- 创建接口UserDao,接口实现类UserDaoImpl,切面类MyAspect,JDK代理类jdkProxy(实现InvocationHandler接口)(也可以成为实现invocationHandler的类,因为这个类中invocationHandler的invoke方法起到了关键作用)
- 在JDK代理类中创建代理方法createProxy,使用java.lang.reflect包中的Proxy代理类的静态方法Proxy.newProxyInstance(类加载器,被代理对象实现的所有接口,实现InvocationHandler的类)返回一个代理类实例(因此真正起作用的是Proxy类,JDK代理类的作用其实是将被代理对象的接口放进去)
- 由因为Proxy.newProxyInstance方法返回一个Object,可以强制类型转换为真实对象的接口UserDao,此时UserDao不再是一个普通的UserDao,而是当它执行方法时,会先调用JDK代理类jdkProxy中实现的invoke方法,并且将被代理的对象,要执行的方法和参数反射填入invoke方法的参数中;
- 而在invoke方法中,我们需要声明一个切面,用于使用增强方法。在真实对象调用方法的前后,我们可以添加增强方法
JdkProxy类
package com.itheima.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.itheima.aspect.MyAspect;
public class JdkProxy implements InvocationHandler {
// 声明目标类接口
private UserDao userDao;
// 创建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
// 1.类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 2.被代理对象实现的所有接口
Class<?>[] clazz = userDao.getClass().getInterfaces();
// 3.使用代理类,进行增强,返回的是代理后的对象;
// 返回代理类的一个实例,返回后的代理类可以当作被代理类Proxy使用
return Proxy.newProxyInstance(classLoader, clazz, this);
}
// 所有动态代理类Proxy的方法调用都交给下面这个重写的invoke()执行
// proxy 被代理的对象
// method 测试类中动态调用方法时,反射将要被执行的方法信息
// args 反射回来的,执行方法时需要的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 声明一个切面,用于使用增强方法
MyAspect myAspect = new MyAspect();
// 前增强,即在调用程序本身方法之前,使用增强方法
myAspect.check_Permissions();
/*
* 根据反射,动态调用程序本身方法 此时用userDao是因为这样可以降低耦合度,即修改代码的难度。
* userDao是接口类,接口类可以引用实现类对象,因此这样无论实现类是谁,都不需要修改这里的代码 体现了面向接口编程的思想
*
**/
method.invoke(userDao, args);
// 后增强
myAspect.log();
return null;
}
}
测试类
package com.itheima.jdk;
public class JdkTest {
public static void main(String[] args) {
// 创建代理对象
JdkProxy jdkProxy = new JdkProxy();
// 创建目标类
UserDao userDao = new UserDaoImpl();
// 从代理对象中获取被增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
// 执行方法
userDao1.addUser();
userDao1.deleteUser();
}
}
执行结果
若使用普通的,没被代理过的对象userDao,则执行结果是