代理模式还是比较容易理解,类比我们生活,电视机厂家生成电视,然后电视机厂家把电视剧买给老百姓。但是老百姓一般是从代理商家那儿买电视剧。其中电视剧厂家就是真正拥有货源的,被叫做真实角色。代理商家其实是没有货源的,不过代理商家在卖电视剧的时候,可以搞很多活动以增加销量,代理商家就是代理角色。
代理模式分为静态代理和动态代理两种。
静态代理:
静态代理一般分为三个角色:
1.抽象角色:一般是接口或者抽象类,里面定义了方法
2.真实角色:需要被代理的类
3.代理对象:代理真实角色,扩展更多的功能。
代码:
抽象角色: 里面定义了需要被代理的方法
public interface TargetObject {
public void sendMes();
}
真实角色:实现了抽象角色
public class ReallyObject implements TargetObject {
@Override
public void sendMes() {
System.out.println("真实对象执行了sendMes()");
}
}
代理对象:在真实角色上,扩展了更多的功能
public class ProxyObject implements TargetObject {
private ReallyObject reallyObject;
public ProxyObject(ReallyObject reallyObject) {
this.reallyObject = reallyObject;
}
@Override
public void sendMes() {
reallyObject.sendMes();
//扩展的内容
System.out.println("发送信息完毕,请及时接受");
}
}
测试:
public static void main(String[] args) {
ReallyObject reallyObject = new ReallyObject();
ProxyObject proxyObject = new ProxyObject(reallyObject);
proxyObject.sendMes();
}
总结:从上述代码,我们也看到了,假如抽象角色改变,那么真实角色和代理角色都必须改变。在实际开发这样使用的话,就非常麻烦了,所有这种方法,我们没怎么使用。
动态代理
动态代理分为jdk动态代理以及CGLIB代理。CGLIB是一个开源的项目。
JDK动态代理
jdk动态代理,怎能代理实现了接口的类,我们需要使用到InvocationHandler类的invoke方法(代理类执行方法的时候,其实是执行invoke方法),所有我们需要重写InvocationHandler类。下面我们以模拟发送短信为例
发送短信接口
public interface SmsService {
void send(String s);
}
发送短信实现类
public class SmsServiceImpl implements SmsService {
@Override
public void send(String s) {
System.out.println("开始发送短信:" + s);
}
}
实现InvocationHandler类,重写invoke方法。注意 我们在new的时候,需要把真实对象(SmsServiceImpl)传入构造函数.这样invoke方法里面才能调用真实对象的方法。
public class OverrideInvocationHandler implements InvocationHandler {
private Object target;
public OverrideInvocationHandler(Object target) {
this.target = target;
}
//当代理对象调用方法的时候,其实是调用InvocationHandler的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("发送短信之前");
Object invoke = method.invoke(target, args);
System.out.println("发送短信之后");
return invoke;
}
测试:
public class TestDynamic {
public static void main(String[] args) {
//通过真实对象获取代理对象
SmsServiceImpl smsService = new SmsServiceImpl();
SmsService service = (SmsService) Proxy.newProxyInstance(smsService.getClass().getClassLoader(),
smsService.getClass().getInterfaces(),
new OverrideInvocationHandler(smsService));
service.send("1234");
}
}
总结:jdk动态代理必须实现接口,接口变动的话,就需要变动其实现类。而且其所有的代码在编译的时候就生成了一个个class文件。
CGLIB动态代理
CGLIB是一个开源框架,所有我们需要引入jar包
<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>7.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
实现CGLIB动态代理需要用到MethodIntercptot类和Enhancer类(代理增强类)。
CGLIB主要使用的是继承,可以直接代理其真实对象。不过效率没有JDK动态代理高。而且是在运行期间,由JVM生成一个个字节码,这和JDK代理是不一样的。
代码:模拟阿里发送短信
真实对象
public class AliSendService {
public void send(String code) {
System.out.println("发送阿里短信:" + code);
}
}
实现MethodInvocationIntercptor类,并重新其方法。
//重写intercept方法
public class CglibDynamicInterception implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("发送短信之前");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("返送短信之后");
return o1;
}
}
实现创建代理类,使用Enhancer类
public class ProxyFactory {
/**
* 获取代理对象
* @param clazz
* @return
*/
public static Object getInstance(Class<?> clazz) {
//代理增强类
Enhancer enhancer = new Enhancer();
//设置拦截器
enhancer.setCallback(new CglibDynamicInterception());
//设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
//设置被代理类
enhancer.setSuperclass(clazz);
//创建代理对象
return enhancer.create();
}
}
测试:
public class CglibTest {
public static void main(String[] args) {
AliSendService sendService = (AliSendService) ProxyFactory.getInstance(AliSendService.class);
sendService.send("1234");
}
}
总结,CGLIB ,是通过是代理的时候,生成子类来拦截被代理对象的执行方法。是在程序运行期间,由JVM生成class文件。可以直接代理真实对象。比如在SpringAOP模块,如何被真实对象实现了接口,则默认采用JDK动态代理,如果没有接口,则采用CGLIB动态代理。