二、结构型模式
1、代理模式
(1)、概述
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:在不改动原有代码的前提下,在已有代码基础上添加新的功能,从而增强原功能。这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
(2)、分类
①、静态代理(静态定义代理类):静态代理实现起来比较简单,需要提前定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。但静态代理需要去实现每一个被代理的类以及方法,如果被代理的方法比较多,会比较繁琐。
②、动态代理(动态生成代理类):
a、JDK动态代理:需要被代理类一定要实现某个接口,而代理类只需要实现InvocationHandler接口。代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)。只能对实现了接口的类生成代理,而不能针对类,对于没有实现接口的类无法实现JDK动态代理。Spring会使用JDK Proxy去创建代理对象,而对于没有实现接口的对象,就无法使用JDK Proxy去进行代理,因为Proxy.newProxyInstance()方法会返回的是一个指定接口的代理类实例,需要用接口接收。
b、cglib动态代理:提供的动态代理不需要类实现了某个接口,是针对类实现代理。cglib采用继承的方式实现动态代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
(3)、静态代理的代码实现
①、定义Automobile接口
public interface Automobile {
String doSomething(String series);
}
②、被代理类实现Automobile接口
public class Benz implements Automobile {
@Override
public String doSomething(String series) {
return String.format("德国奔驰:%s",series);
}
}
③、代理类实现Automobile接口
public class BeijingBenz implements Automobile {
private Benz benz;
public BeijingBenz(Benz benz){
this.benz = benz;
}
@Override
public String doSomething(String series) {
System.out.println("我是北京奔驰,我代理德国奔驰");
String msg = benz.doSomething(series);
System.out.println(msg);
return msg;
}
}
④、实现静态代理
public class Main {
public static void main(String[] args) {
Benz benz = new Benz();
BeijingBenz beijingBenz = new BeijingBenz(benz);
beijingBenz.doSomething("S600");
}
}
⑤、实际应用
TokenCallable通过代理Callable实现token的传递,以及日志打印的一些信息。
public class TokenCallable<V> implements Callable<V> {
private final String accessToken;
/**
* 被代理的Callable
*/
private final Callable<V> delegate;
private final String taskName;
public TokenCallable(Callable<V> delegate, String accessToken,String taskName) {
this.delegate = CallableWrapper.of(delegate);
this.accessToken = accessToken;
this.taskName = taskName;
}
@Override
public V call() throws Exception {
long start = System.currentTimeMillis();
LogUtil.debug("currentThreadId = " + Thread.currentThread().getId() + ",accessToken=" + accessToken);
//给当前线程设置token
AccessTokenUtil.setAccessToken(accessToken);
V call = delegate.call();
LogUtil.info("taskName =" + taskName + ",taskTime = " + (System.currentTimeMillis() - start) + " ms");
return call;
}
}
(4)、JDK动态代理的代码实现
①、定义Automobile接口
public interface Automobile {
String doSomething(String series);
}
②、被代理类实现Automobile接口
public class BMW implements Automobile {
@Override
public String doSomething(String series) {
return String.format("德国宝马 %s", series);
}
}
③、代理类实现InvocationHandler接口
public class HuachenBMW implements InvocationHandler {
public Automobile target;
public HuachenBMW(Automobile target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("来中国混,根据指示找代理");
Object ret = method.invoke(target, args);
System.out.println("代理车系是:" + ret);
return ret;
}
}
④、实现JDK动态代理
public class Main {
public static void main(String[] args) {
BMW BMW = new BMW();
HuachenBMW huachenBMW = new HuachenBMW(BMW);
//通过Proxy.newProxyInstance()方法返回一个指定接口的代理类实例
Automobile automobile = (Automobile)Proxy.newProxyInstance(BMW.getClass().getClassLoader(),
BMW.getClass().getInterfaces(),
huachenBMW);
String series = automobile.doSomething("X6");
System.out.println("抢到一辆:"+ series);
}
}
(5)、cglib动态代理的代码实现
①、引入cglib的jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
②、定义被代理类
public class Audi {
public String doSomething(String series){
System.out.println("原产于德国的奥迪汽车");
return String.format("德国奥迪,车系是:%s", series);
}
}
③、代理类实现MethodInterceptor接口
public class FirstAudi implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理德国奥迪汽车");
Object series = methodProxy.invokeSuper(o, objects);
System.out.println(series);
return series;
}
}
④、实现cglib动态代理
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Audi.class);
enhancer.setCallback(new FirstAudi());
Audi audi = (Audi) enhancer.create();
audi.doSomething("A8");
}
}
注:JDK内置动态代码采用Java反射机制实现,cglib库实现的动态代理,采用的是ASM直接操作字节码方式实现。由于反射操作的效率要低于直接操作字节码的效率,所以,cglib实现动态代理效率上比JDK内置的动态代理要好。