动态代理
什么是代理?
例如:
public void eat(){
System.out.println("吃");
}
我们只想要吃饭也就是执行eat()方法,但是在吃饭之前我们需要做饭,盛饭等一些复杂操作,如果直接在eat方法里面添加做饭盛饭的代码(称为侵入式),这种方式在实际开发中可能会破坏源代码,我们此时就会需要代理(也就是外卖),也就是将做饭,盛饭这些事情都交由外卖这个代理,我们只需要吃就行。
//写一个接口,Me和WaiMai都需要实现这个接口,接口里的方法就是,Me类需要被代理的方法,也是WaiMai可以代理的方法.
public interface Nic {
public abstract void eat();
}
准备Me类:
public class Me implements Nic {
@Override
public void eat() {
System.out.println("吃饭");
}
}
代理类:
public class WaiMai implements Nic {
private Me me ;
public WaiMai() {
this.me = new Me();
}
@Override
public void eat() {
System.out.println("送达");
me.eat();
}
}
调用:
public class Main {
public static void main(String[] args) {
Nic waiMai = new WaiMai();
waiMai.eat();
}
}
/*结果:
送达
吃饭
*/
代理:就是对象不想干的事,委托给其他对象做。
代理的好处:可以使得真实角色的操作更加纯粹,只用专注于具体的业务,不用关注与公共的业务。
静态代理的弊端:需要自己编写,一个接口的被代理类就需要一个代理类,代码冗余;无法代理所有的方法,需要一个一个的写方法代理。例如我们还有想要买其他东西(药物)也希望外卖可以直接送达,此时就需要在接口、代理类、被代理类中都添加买药的方法。
想要避免以上的弊端就产生了动态代理:
动态代理
在以上的代理中我们既需要外卖送饭,有需要外码送药物使用动态代理如下:
jdk动态代理:
准备接口:
public interface Nic {
void eat();
void buyMedicine();
}
准备被代理类:
public class Me implements Nic{
@Override
public void eat() {
System.out.println("我肚子饿,吃饭");
}
@Override
public void buyMedicine() {
System.out.println("我生病了,买药");
}
}
准备代理类,代理类需要实现InvocationHandler接口:
public class WaiMaiHandler implements InvocationHandler {
private Me me;
public WaiMaiHandler(Me me) {
this.me = me;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke;
//增强代码,一遍添加日志,开始事务等
if ("buyMedicine".equals(method.getName())){
System.out.println("外码送药");
}else {
System.out.println("外码送餐");
}
//动态调用被代理方法,动态代理的本质是使用反射
return method.invoke(me,args);
}
}
测试:
public static void main(String[] args) {
Me me = new Me();
WaiMaiHandler waiMaiHandler = new WaiMaiHandler(me);
//nic就是自动生成的动态代理类
Nic nic = (Nic) Proxy.newProxyInstance(me.getClass().getClassLoader(), me.getClass().getInterfaces(), waiMaiHandler);
nic.eat();
nic.buyMedicine();
}
/*结果
外码送餐
我肚子饿,吃饭
外码送药
我生病了,买药
*/
/**
三个参数分别是
类加载器 (被代理类的类加载器me.getClass().getClassLoader())
被代理类的接口类型 (被代理类所有实现的接口me.getClass().getInterfaces())
代理类 一个InvocationHandler的实现(waiMaiHandler)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
*/
cglib动态代理
首先静态代理和jdk动态代理都需要一个接口,而cglib不需要接口
准备被代理类:
public class Me {
public void eat(){
System.out.println("我饿了,吃饭");
}
public void buyMedicine(){
System.out.println("我病了,买药");
}
}
准备代理类 , 代理类需要实现MethodInterceptor接口:
public class WaiMaiMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
}
测试:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//继承被代理类
enhancer.setSuperclass(Me.class);
//设置回调
enhancer.setCallback(new WaiMaiMethodInterceptor());
//设置代理类对象
Me me = (Me) enhancer.create();
//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
me.eat();
me.buyMedicine();
}
cglib动态代理和jdk动态代理的区别
都在运行期间产生字节码文件
cglib动态代理是不需要接口的,是通过继承被代理类实现的,所以被代理方法不能有final修饰。
jdk动态代理是通过实现接口来实现的。