版权声明:如有转载,请标明出处,谢谢合作! https://blog.csdn.net/lyc_liyanchao/article/details/83240498
在分析AOP的源码之前,我们需要了解一下代理模式,代理模式可分为静态代理和动态代理两种。
1.静态代理
代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理。
静态代理由 业务实现类、业务代理类 两部分组成。业务实现类 负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理,主要是在方法中首先进行预处理动作,然后调用业务实现类的方法,还可以规定调用后的操作。我们在需要调用业务时,不是直接通过业务实现类来调用的,而是通过业务代理类的同名方法来调用被代理类处理过的业务方法。
- 代理类和被代理类
package com.lyc.cn.v2.day04.aop.proxy;
/**
* 账户接口
*/
public interface Count {
// 查询账户
void queryCount();
// 修改账户
void updateCount();
}
package com.lyc.cn.v2.day04.aop.proxy;
/**
* @author: LiYanChao
* @create: 2018-10-21 12:07
*/
public class CountImpl implements Count {
@Override
public void queryCount() {
System.out.println("==查询账户");
}
@Override
public void updateCount() {
System.out.println("==更新账户");
}
}
package com.lyc.cn.v2.day04.aop.proxy;
/**
* 代理类
* @author: LiYanChao
* @create: 2018-10-21 12:08
*/
public class CountProxy implements Count {
private CountImpl countImpl;
/**
* 覆盖默认构造器
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("==查询账户开始");
// 调用真正的查询账户方法
countImpl.queryCount();
System.out.println("==查询账户结束");
}
@Override
public void updateCount() {
System.out.println("==更新账户开始");
// 调用真正的修改账户操作
countImpl.updateCount();
System.out.println("==更新账户结束");
}
}
- 测试及结果
@Test
public void test3() {
// 静态代理
CountImpl countImpl = new CountImpl();
CountProxy countProxy = new CountProxy(countImpl);
countProxy.updateCount();
System.out.println("\n*******\n");
countProxy.queryCount();
}
==更新账户开始
==更新账户
==更新账户结束
*******
==查询账户开始
==查询账户
==查询账户结束
- 总结
一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。
2.JKD动态代理
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。JDK的动态代理需要实现InvocationHandler接口,并重写invoke方法。
- 代理类和被代理类
package com.lyc.cn.v2.day04.aop.jdk;
public interface Animal {
void say();
}
package com.lyc.cn.v2.day04.aop.jdk;
public class Dog implements Animal {
@Override
public void say() {
System.out.println("I am a dog...");
}
}
package com.lyc.cn.v2.day04.aop.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
/**
* 构造方法
* @param target 目标对象
*/
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
/**
* @param proxy JDK动态生成的最终代理对象
* @param method 调用真实对象的某个方法的Method对象
* @param args 调用真实对象某个方法时接受的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("==代理方法开始执行");
Object invoke = method.invoke(target, args);
System.out.println("==代理方法结束执行");
return invoke;
}
/**
* 获取目标对象的代理对象
* @return 代理对象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
}
}
- 测试及结果
@Test
public void test4() {
// JDK动态代理
MyInvocationHandler handler = new MyInvocationHandler(new Dog());
Animal proxy = (Animal) handler.getProxy();
proxy.say();
}
==代理方法开始执行
I am a dog...
==代理方法结束执行
- 总结
JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。如果没有接口定义又想使用动态代理的话,那么可以使用CGLIB动态代理。
3.CGLIB动态代理
CGLIB是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 在使用的时候需要引入cglib和asm的jar包
compile group: 'asm', name: 'asm', version: '3.3.1'
compile group: 'cglib', name: 'cglib', version: '2.2.2'
- 代理类和被代理类
package com.lyc.cn.v2.day04.aop.cglib;
public class Cat {
public void say() {
System.out.println("I am a cat");
}
}
package com.lyc.cn.v2.day04.aop.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
public Object getInstance(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 返回代理对象
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("==代理方法开始执行");
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("==代理方法结束执行");
return result;
}
}
- 测试类及结果
@Test
public void test5() {
// CGLIB动态代理
Cat cat = (Cat) new CglibProxy().getInstance(Cat.class);
cat.say();
}
==代理方法开始执行
I am a cat
==代理方法结束执行
- 总结
1、静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
2、JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
3、CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;