一.什么是代理模式
1.代理(proxy)是一种设计模式,提供了可通过代理对象访问目标对象的功能,这样做的好处在于:可以在目标对象功能实现的基础上,增加额外的功能补充,扩展目标对象的功能。
二.静态代理
在静态代理中,代理对象和目标对象都必须实现同一个接口或继承同一个父类,因此要定义一个接口或抽象类。
package Test.Proxy;
public interface IUserDao {
void save();
}
package Test.Proxy;
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("已保存数据");
}
}
package Test.Proxy;
public class UserProxy implements IUserDao {
private IUserDao target;
public UserProxy(IUserDao iuseDao){
this.target = iuseDao;
}
public void save(){
System.out.println("开启事物...");
target.save();
System.out.println("关闭事物...");
}
public static void main(String[] args){
UserDao userDao = new UserDao();
UserProxy userProxy = new UserProxy(userDao);
userProxy.save();
}
}
静态代理:可以做到不修改目标类的代码情况下,实现功能的扩展。
也存在一些问题,因为代理对象和目标对象都要实现同一个接口,或者继承同一个父类,所以当代理类太多时,修改接口就会需要维护代理对象,加大工作量。
三.动态代理
动态代理主要是在程序运行时jvm才为被代理对象生成代理对象。
动态代理是在程序运行时通过反射机制动态创建的。主要是用到了java.lang.reflect.Proxy类和InvocationHandler接口。
JDK代理是常用的一种动态代理方式,JDK中生成代理对象的代理类就是Proxy。
//目标类接口
interface IDog{
void run();
}
//目标类
class GunDog implements IDog{
@Override
public void run() {
System.out.println("猎狗在跑");
}
}
class DogUtils{
public static void method1() {
System.out.println("增强方式一");
}
public static void method2() {
System.out.println("增强方式二");
}
}
class MyInvocationHandle implements InvocationHandler{
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
DogUtils.method1();
method.invoke(target, args);
DogUtils.method2();
return null;
}
}
//生产代理对象的工厂
class MyProxyFactory{
public static Object getProxy(Object target) {
MyInvocationHandle handle = new MyInvocationHandle();
handle.setTarget(target);
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handle);
return proxy;
}
}
public class ProxyDemo {
public static void main(String[] args) {
IDog dog = new GunDog();
IDog proxy =(IDog) MyProxyFactory.getProxy(dog);
proxy.run();
}
JDK代理,代理类不需要实现接口,但目标对象所在的类必须要实现接口,否则无法使用JDK动态代理。
CGLIB代理:
上面的静态代理和动态代理模式有个相同点就是都要求目标对象是实现一个接口的对象,然而并不是任何对象都会实现一个接口,也存在没有实现任何的接口的对象,
这时就可以使用继承目标类以目标对象子类的方式实现代理,这种方法就叫做:Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
使用JDK动态代理有一个限制,就是被代理的对象必须实现一个或多个接口,若想代理没有实现接口的类,就需要使用Cglib实现.
需要asm和cglib 这两个jar包。
需要注意的是,cglib原理是通过字节码技术为一个类创建子类,并在子类拦截所有父类方法的调用,顺势织入横切逻辑,但因为采用的是继承,所以不能对final修饰的类进行代理。
public class CglibProxy {
public static void main(String[] args) {
int[] arr = new int[100000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 1000);
}
//实例化一个增强器,也就是cglib中的一个class generator
Enhancer enhancer = new Enhancer();
//设置目标类
enhancer.setSuperclass(ArraySort2.class);
//设置拦截对象,这里直接使用匿名内部类写法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object , Method method, Object[] args, MethodProxy proxy) throws Throwable {
String sortName = method.getName();
switch (sortName) {
case "bubbleSort":
sortName = "冒泡排序";
break;
case "selectSort":
sortName = "选择排序";
break;
case "quickSort":
sortName = "快速排序";
break;
default:
break;
}
long start = System.currentTimeMillis();
//此处一定要使用proxy的invokeSuper方法来调用目标类的方法
proxy.invokeSuper(object, args);
long end = System.currentTimeMillis();
System.out.println("本次" + sortName + "的执行时间为: " + (end -start) + "ms");
return null;
}
});
//生成代理类并返回一个实例
ArraySort2 arraySort = (ArraySort2) enhancer.create();
arraySort.bubbleSort(arr);
arraySort.selectSort(arr);
arraySort.quickSort(arr);
}
}
class ArraySort2{
public void quickSort(int[] arr) {
Arrays.sort(arr);
}
public void selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
在springAOP面向切面中:
如果目标对象实现了接口,可以用jdk动态代理。
如果没有实现接口,使用cglib动态代理。