一、静态代理
(一)代理模式概述
在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法来实现代理模式。
我直接就是用老师举的例子
上图的三个角色:
1、真实对象(能够完成业务的):局长;
2、代理对象角色:秘书;
3、抽象角色(抽象类或者接口,真实对象和代理对象角色都继承/实现抽象角色):上图没有
public interface AbstractBean {
/**
* dosomething
*/
void doSomething();
}
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class JuzhangBean implements AbstractBean{
private String name;
@Override
public void doSomething() {
System.out.println("局长dosomething");
}
}
public class MishuBeanTest {
@Test
public void testStaticProxy() {
//真实对象
JuzhangBean juzhangBean = new JuzhangBean().setName("王英英");
//代理对象角色
MishuBean mishuBean = new MishuBean(juzhangBean).setName("鼠鼠");
//不认识但是认识秘书,所以找秘书办事,但是最后依然是局长拍板
mishuBean.doSomething();
}
}
下面演示一下附加功能,比如说在办事情之前代理对象(秘书)要约你一起大保健,那自然是得咱们请客,结果他找完局长之后完了之后还要去洗个脚,我自然就是不能让他白忙活。
//这里是秘书的doSomething方法
@Override
public void doSomething() {
System.out.println("Before:保个健先");
System.out.println("_____________");
System.out.println("秘书dosomething");
juzhangBean.doSomething();
System.out.println("_____________");
System.out.println("After:洗个脚先");
}
可见代理对象可以在不修改原有类的基础上给业务额外添加功能:
二、动态代理
(一)动态代理概述
静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式 的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
(二)JDK 动态代理
所谓jdk动态代理,是使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy。
使用 JDK 动态代理首先要创建一个 ProxyHandler 类,让他来实现 InvocationHandler 接口,实现之后会提示你重写里面的 invoke() 方法,这个 invoke 方法里面就是被代理对象需要增强的方法了。
invoke(proxy:就是代理对象、newProxyInstance方法的返回对象, method调用的方法,args方法中的参数)
定义完成之后使用 Proxy.newProxyInstance,也就是在运行期间生成代理类,其返回值是需要由被代理类所实现的接口来接收,接收到的就是产生的代理对象。
Proxy.newProxyInstance(类加载器, 目标类实现的接口, InvocationHandler对象)
当代理对象被调用,会调用 InvocationHandler 实现类中的 invoke 方法来达到增强的目的。
需要注意,JDK 代理只能针对接口做代理
public interface AbstractBean {
/**
* dosomething
*/
void doSomething();
}
@Data
@Accessors(chain = true)
public class JuzhangBean implements AbstractBean{
private String name;
@Override
public void doSomething() {
System.out.println("局长dosomething");
}
}
public class JdkProxy implements InvocationHandler {
//传入真实对象
private AbstractBean abstractBean;
public JdkProxy(AbstractBean abstractBean) {
this.abstractBean = abstractBean;
}
/**
* 代理对象调用真实对象的时候执行的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before真实对象执行之前");
//真实对象的方法
method.invoke(abstractBean,args);
System.out.println("After真实对象执行之后");
return null;
}
}
我尼玛这玩意是真难看懂啊
@Test
public void testJdk() {
//真实对象
JuzhangBean juzhangBean = new JuzhangBean().setName("局长");
JdkProxy jdkProxy = new JdkProxy(juzhangBean);
//创建代理类、
//参数:ClassLoader 当前类加载器,interface代理类要实现的接口 Invocationhandler接口,不过需要被实现写增强方法
AbstractBean proxyAbstractBean = (AbstractBean)Proxy.newProxyInstance(juzhangBean.getClass().getClassLoader(), new Class[]{AbstractBean.class}, jdkProxy);
//调用代理对象
proxyAbstractBean.doSomething();
}
关于为什么 JDK 代理只能代理有接口的类:JDK 动态代理会在程序运行期间生成一个叫 $Proxy0 的代理类,这个类继承自 java.lang.reflect.Proxy 并且会实现被代理类的接口,每个动态代理类都继承了一个 Proxy ,在 Java 里面又是不支持多继承的,在上面这个例子中产生的代理类大概是这样的。这玩意本来就继承自 extends 类了,就不能再继承别的类,只能去实现接口了。
public final class $Proxy0 extends Proxy implements AbstractBean{}
动态代理本身就是为了对原始的实现进行拦截并做功能和拓展,在实际开发中大多是面向接口的,所以这样合情合理。
(三)cglib 动态代理
上面我们分析过 JDK 动态代理只能代理有接口的类,但是有的类是没有接口的,这时候我们可以使用 cglib 动态代理。Spring 框架本身就依赖 cglib 的 jar 包。
同样需要定义一个代理类 CglibProxy 实现 MethodInterceptor 接口
@Data
public class Juzhang {
private String name;
public void doSomething() {
System.out.println("dosomething");
}
public int do1 () {
System.out.println("do1");
return 1000;
}
}
public class CglibProxy implements MethodInterceptor {
//传入真实对象
private Juzhang juzhang;
public CglibProxy(Juzhang juzhang) {
this.juzhang = juzhang;
}
Enhancer enhancer = new Enhancer();
/**
* 获得代理类对象得方法
* @return
*/
public Juzhang getProxy() {
enhancer.setSuperclass(juzhang.getClass());
enhancer.setCallback(this);
return (Juzhang)enhancer.create();
}
/**
* 和 invoke 方法的作用一样
* @param o
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before");
Object result = method.invoke(juzhang,objects);
System.out.println("After");
return result;
}
}
@Test
public void testJdk() {
//真实对象
JuzhangBean juzhangBean = new JuzhangBean().setName("局长");
JdkProxy jdkProxy = new JdkProxy(juzhangBean);
//创建代理类
AbstractBean proxyAbstractBean = (AbstractBean) Proxy.newProxyInstance(juzhangBean.getClass().getClassLoader(), new Class[]{AbstractBean.class}, jdkProxy);
// 调用代理对象
proxyAbstractBean.doSomething();
}
(四)总结
JDK 动态代理实现接口,CGLIB 动态继承思想。
JDK 动态代理只想效率高于 cglib。
如果对象有接口实现,选择 JDK 代理,没有实现接口选择 CGLIB 代理。
标签:对象,Spring03,代理,接口,System,模式,println,public From: https://www.cnblogs.com/purearc/p/17321207.html