目录
静态代理
/**
* Created by leboop on 2020/5/25.
*/
public interface IStar {
void sing(String songName);
}
/**
* Created by leboop on 2020/5/25.
*/
public class PopStar implements IStar {
@Override
public void sing(String songName) {
System.out.println("sing "+songName);
}
}
/**
* Created by leboop on 2020/5/25.
*/
public class StarProxy implements IStar {
private IStar star;
public StarProxy() {
}
public StarProxy(IStar star) {
this.star = star;
}
@Override
public void sing(String songName) {
preSing();
star.sing(songName);
afterSing();
}
public void preSing() {
System.out.println("preparation before singing");
}
public void afterSing() {
System.out.println("some work after singing");
}
}
/**
* Created by leboop on 2020/5/25.
*/
public class StaticProxyMain {
public static void main(String[] args) {
IStar star=new PopStar();
StarProxy starProxy=new StarProxy(star);
starProxy.sing("江南style");
}
}
程序输出结果如下:
preparation before singing
sing 江南style
some work after singing
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。比如我们并没有修改目标对象PopStar的sing方法的功能,对该功能进行了扩展:preSing()和afterSing();
缺点:代理对象StarProxy需要与目标对象PopStar实现同样的接口IStar,如果扩展的功能比较多,一个代理类扩展一个功能,会有很多代理类。同时,一旦接口IStar增加方法,目标对象与代理对象都要修改代码进行维护。
动态代理可以解决如上问题。
动态代理
依然使用上面的IStar接口和其实现类PopStar。后面我们会看到动态代理一个限制就是被代理的对象需要是某个接口的实现,比如PopStar对象是IStar接口的实现。JDK中为我们提供了Proxy代理类和InvocationHandler接口,我们在实现动态代理的时候使用这两个已有的东西。我们将目标对象方法前后需要做的工作封装到了StarBroker类中,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class StarBroker {
public static void preSing() {
System.out.println("preparation before singing");
}
public static void afterSing() {
System.out.println("some work after singing");
}
}
下面创建一个PopStarHandler实现InvocationHandler接口,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class PopStarHandler implements InvocationHandler {
// 真实对象
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
PopStarBroker.preSing();
Object object=method.invoke(this.target,args);
PopStarBroker.afterSing();
return object;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
PopStarHandler类中的target对象就是被代理的对象,invoke方法就是对目标对象功能的扩展。下面需要定义了生成代理对象的工厂类,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class ProxyFactory {
public static Object getProxy(Object target) {
PopStarHandler handler = new PopStarHandler();
handler.setTarget(target);
// target需要有接口
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
return proxy;
}
}
getProxy通过传入一个被代理对象,然后产生一个代理对象。这里需要将被代理对象target设置到handler中,最后通过调用JDK中已有的Proxy类产生我们需要的代理对象。注意到newProxyInstance方法中传入了target.getClass().getInterfaces()参数,这就是说需要被代理对象是某个接口的实现。下面就可以在客户端使用代理对象了,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class DynamicProxyMain {
public static void main(String[] args) {
// 真实对象
IStar star=new PopStar();
// 代理对象
IStar starProxy=(IStar)ProxyFactory.getProxy(star);
starProxy.sing();
}
}
上面是一种实现方式。也可以去掉PopStarHandler 和ProxyFactory 这两个类,直接在客户端实现,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class DynamicProxyMain {
public static void main(String[] args) {
// 真实对象
IStar star = new PopStar();
// 代理对象
IStar starProxy = (IStar) Proxy.newProxyInstance(
star.getClass().getClassLoader(),
star.getClass().getInterfaces(),
(proxy, method, methodArgs) -> {
if ("sing".equals(method.getName())) {
StarBroker.preSing();
Object object = method.invoke(star, methodArgs);
StarBroker.afterSing();
return object;
}
return null;
});
starProxy.sing("江南style");
}
}
这段代码中使用了匿名内部类。构造动态代理的核心方法newProxyInstance如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 克隆一份接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
}
基本思路:
(1)先克隆一份被代理对象PopStar的接口IStar
final Class<?>[] intfs = interfaces.clone();
(2)根据克隆的接口和类加载器获取到类字节码
Class<?> cl = getProxyClass0(loader, intfs);
(3)由类字节码获取构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
(4)构造方法创建实例并绑定扩展原有功能的对象h
cons.newInstance(new Object[]{h});
优点:动态代理的代理对象不需要实现接口(比如IStar),每次在运行时动态生成。
缺点:被代理的对象需要实现接口,因为并不是所有的对象都有接口。
cglib代理可以解决被代理对象需要实现接口的限制。
cglib代理
cglib依赖两个jar包:cglib和asm,可以通过如下依赖添加:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
先定义一个ClassicalStar类,如下:
/**
* Created by leboop on 2020/5/25.
*/
public class ClassicalStar {
public void sing(String songName){
System.out.println("sing "+songName);
}
}
该类未实现任何接口,客户端代码通过如下方式调用:
/**
* Created by leboop on 2020/5/25.
*/
public class CglibMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ClassicalStar.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
StarBroker.preSing();
Object result = proxy.invokeSuper(obj, args);
StarBroker.afterSing();
return result;
}
});
ClassicalStar star = (ClassicalStar) enhancer.create();
star.sing("江南style");
}
}
同样能完成静态代理和动态代理的功能。