1 静态代理
静态代理实现:
1.1 接口
package com.yyy;
public interface Animals {
public void eat();
public void say();
}
1.2 实现类
package com.yyy;
public class Cat implements Animals{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
1.3 代理实现类
package com.yyy;
public class ProxyCat implements Animals{
private Cat cat = new Cat();
@Override
public void eat() {
System.out.println("这是静态代理");
cat.eat();
}
}
静态代理设计模式的特点:
优点:结构清晰,易于理解
缺点:如果被代理者有多个方法,则代理者也需要开发多个方法,其中往往存在大量重复代码,仍然存在代码重复。
静态代理设计模式解决了软件分层过程中额外的功能代码侵入模块的问题,将额外的功能代码提取到了代理者中进行,但是静态代理实现的代理者中存在大量重复的代码,并没有解决代码重复问题。所以在真正开发中–包括spring的底层,基本不会使用静态代理。
2 动态代理
2.1 jdk内置的动态代理
在jdk中提供了动态代理实现的工具类,直接使用该工具类就可以创建出代理者,并且可以通过内置的回调函数指定代理在工作时的执行逻辑,从而实现基于jdk原生api的动态代理机制。
package com.yyy;
public interface Animals {
public void eat();
public void say();
}
package com.yyy;
public class Cat implements Animals{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
package com.yyy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicAnimals implements InvocationHandler {
private Cat cat;
public DynamicAnimals(Cat cat){
this.cat = cat;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是动态代理");
method.invoke(cat,args);
return null;
}
}
package com.yyy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
InvocationHandler ds = new DynamicAnimals(cat);
Animals animals = (Animals) Proxy.newProxyInstance(cat.getClass().getClassLoader(),cat.getClass().getInterfaces(),ds);
animals.eat();
}
}
运行结果:
这是动态代理
猫吃鱼
这是另一种写法:
package com.yyy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
/**
* classLoader:用来生成代理者类的类加载器,通常可以传入被代理者类的类加载器
* interfaces: 要求生成的代理者实现的接口们,通常就是实现和被代理者相同的接口,保证具有和被代理者相同的方法
* nvocationHandler: 用来设定回调函数的回调接口,使用者需要写一个类实现此接口,从而实现其中的invoke方法,
* 在其中编写代码处理代理者调用方法时的回调过程,通常在这里调用真正对象身上的方法,并且在方法之前或之后做额外操作。
*/
Animals proxy = (Animals) Proxy.newProxyInstance(cat.getClass().getClassLoader(), cat.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是动态代理");
method.invoke(cat,args);
return null;
}
});
proxy.eat();
}
}
Java动态代理的特点:
优点:
不需要像静态理一样被代理方法都要实现一遍,而只需要在回调函数中进行处理就可以了,重复代码只需编写一次。
缺点:
java的动态代理是通过代理者实现和被代理者相同的接口来保证两者具有相同的方法的,如果被代理者想要被代理的方法不属于任何接口,则生成的代理者自然无法具有这个方法,也就无法实现对该方法的代理。
所以java的动态代理机制是基于接口进行的,受制于要代理的方法是否有接口的支持。
2.2 第三方包Cglib实现的动态代理
CGLIB是第三方提供的动态代理的实现工具,不管有没有接口都可以实现动态代理。
CGLIB实现动态代理的原理是生成的动态代理是被代理者的子类,所以代理者具有和父类即被代理者相同的方法,从而实现代理,这种方式基于继承,不再受制于接口。
package com.yyy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
//增强器
Enhancer enhancer = new Enhancer();
//设定接口 --此方法要求生成的动态代理额外实现指定接口们 ,单cglib动态代理不是靠接口实现的,所以可以不设置
enhancer.setInterfaces(cat.getClass().getInterfaces());
//设定父类 --此处要传入被代理者的类,cglib是通过集成被代理者的类来持有和被代理者相同的方法的,此方法必须设置
enhancer.setSuperclass(cat.getClass());
//设定回调函数 --为增强器设定回调函数,之后通过增强器生成的代理对象调用任何方法都会走到此回调函数中,实现调用真正被代理对象的方法的效果
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("这是Cglib动态代理");
method.invoke(cat,args);
return null;
}
});
//生成代理对象
Animals proxy = (Animals) enhancer.create();
proxy.eat();
}
}
CGLIB动态代理的特点:
优点:无论是否有接口都可以实现动态代理,使用场景基本不受限
缺点:第三方提供的动态代理机制,不是原生的,需要导入第三方开发包才可以使用。