AOP的动态代理
AOP的动态代理
该文章代码来源:
JDK动态代理-B站学习视频
CGLI动态代理-B站学习视频
AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP动态代理的方式主要分为两种:
- CGLIB
- JDK动态代理
动态代理
简介:
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作
JDK动态代理
JDK的动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring会默认使用JDK的动态代理来实现AOP
Proxy类
JDK1.8的官方文档的介绍是:Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
第一版代码
创建一个接口(接口是JDK代理必不可少的一环)
public interface UserDao {
public int addUser();
public void editUser();
}
创建一个实现类
public class UserDaoImpl implements UserDao{
@Override
public int addUser() {
System.out.println("添加用户");
return 0;
}
@Override
public void editUser() {
System.out.println("修改用户");
}
}
我们先看看没有加动态代理之前的打印
public class JdkTest {
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
userDao.addUser();
}
}
就是普通的功能打印
第二版代码
创建一个增强功能的类
public class MyAspect {
public void check_permission(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
创建动态代理类
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为:
- 代理接口是由代理类实现的接口
- 代理实例是代理类的一个实例
- 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler
- 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组
public class MyInvocationHandler implements InvocationHandler {
//代理的动态对象
private Object obj;
//给其一个有参的构造器
public MyInvocationHandler(Object obj){
this.obj=obj;
}
/**
* @param proxy 代理类的实例对象
* @param method 用户请求的方法
* @param args 这是该方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyAspect myAspect = new MyAspect();
myAspect.check_permission();//前置增强
Object result = method.invoke(obj, args);
myAspect.log();//后置增强
return result;
}
}
使用MyAspect类的方法对被代理类进行了前后置的增强
Method类
java.lang.reflect.Method
reflect(反射),该类是在反射包下的
其功能是:提供类或者接口的信息就可以访问调用对应的方法
invoke
作用:调用所提供的方法以及调用方法的参数来完成动态调用
重新编写测试类
public class JdkTest {
public static void main(String[] args) {
// 代理的目标对象
UserDaoImpl userDao = new UserDaoImpl();
// 调用织入代理类的Handler
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userDao);
// 调用JDK动态代理的 proxy代理方法 动态生成原始类实例实现接口的动态代理类实例 返回动态代理类的实例
/**
* newProxyInstance(ClassLoader loader,Class<?>[] interfaces,Invocation,Handler h)
* classloader:用来加载动态生成的字节码的
* interface:被代理对象所有的接口(接口数组)
* handler:教我们如何写实现
*/
UserDao UserDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
UserDaoImpl.class.getInterfaces(),
myInvocationHandler);
// 调用方法
UserDaoProxy.addUser();
//UserDaoProxy是Proxy的子类
System.out.println(UserDaoProxy instanceof Proxy);
}
}
newProxyInstance()
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
参数:
loader - 类加载器来定义代理类
interfaces - 代理类实现的接口列表
h - 调度方法调用的调用处理函数
添加代理后的打印
完成了前置和后置的增强,打印的true表示虽然调用的是被代理类的方法,但是真正被调用的是代理类,是代理类代替了被代理类完成了操作
JDK动态代理,主要实现的是对Jdk只能实现对接口的动态代理,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以用CGLib动态代理
CGLib动态代理
因为JDK动态代理的局限性,因为JDK动态代理的对象必须实现一个或多个接口,因此CGLib代理产生了,CGlib是为了解决没有实现接口的类
CGLIB
CGLIB(Code Generation Library)是一个开源项目。
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
开始编码
为了与接口做区分这次直接创建一个类
导包
使用CGLIB是需要导包的
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
创建实体类
/**
*目标类 被代理类
*/
public class UserDao {
public void saveUser(){
System.out.println("新增用户");
}
}
功能增强类
public class MyAspect {
public void check_permission(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
代理类
/**
* 代理类
*/
public class CglibProxy implements MethodInterceptor {
// 被代理对象 目标对象
private Object target;
public Object createProxy(Object target){
this.target=target;
//最核心的代理 通过cglib增强代码
// 用来生成代理的 工厂类
Enhancer enhancer = new Enhancer();
// 生成目标类对象的子类 进行增强
// 设置被代理的类的类型type
enhancer.setSuperclass(target.getClass());
// 如何增强
// 需要传入一个Callback接口的实现,MethodInterceptor就是Callback接口的子接口
enhancer.setCallback(this);
// 生成代理类的字节码文件
Object proxy = enhancer.create();
return proxy;
}
/**
* 描述如何增强父类
* @param proxy 代理对象 引用
* @param method 被代理对象方法的描述引用
* @param args 方法的参数
* @param methodProxy 代理对象的 对目标对象的方法描述
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 增强代码
MyAspect aspect = new MyAspect();
aspect.check_permission(); //前置增强
// 调用原始方法 这两个方法功能一样
//1.代理类对象进行调用
Object invokeSuper = methodProxy.invokeSuper(proxy, args);
//2.原对象
// Object invoke = method.invoke(target, args);
aspect.log();//后置增强
return invokeSuper;
}
}
对指定的类生成一个子类,覆盖其中的方法,对其进行增强
测试类
public class CgilbTest {
public static void main(String[] args) {
// 首先需要原对象
UserDao target = new UserDao();
// 创建 一个 proxy工厂实例
CglibProxy cglibProxy = new CglibProxy();
UserDao proxy = (UserDao) cglibProxy.createProxy(target);
proxy.saveUser();
}
}
代码打印结果
两种代理的区别
JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进行,通过修改其字节码生成子类来处理
- 需要代理的目标如果实现了接口,默认情况一定是采用JDK的动态代理来实现AOP
- 如果需要代理的目标没有实现接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换
wudidedafeiji: 你好,我查看端口,没用18083端口怎么办
是程小姐呀: https://blog.csdn.net/qq_24845091/article/details/108516290是不是版本问题,遇到报错就打开百度复制粘贴
森林老虎: NoSuchMethodError: org.apache.poi.ss.usermodel.WorkbookFactory.create(Z)Lorg/apache/poi/ss/usermodel/Workbook;报这个错是什么问题
Saneinup: 第8点:实例变量可以实现让多个对象共享内存 这个每创建一个对象会新产生新的实例吧,博主是不是少写了个不字
土豆你个番茄: 我想问下生成的表格,能不能使用公式做一些总结统计的功能?