一.什么是代理模式?
举个简单的例子就是比如你相亲的女孩想跟你要个10W彩礼,但是她不好意思直接跟你说啊。这时候就媒婆就说我去跟小伙子说。有什么回应我告诉你。然后媒婆就先夸女方怎么怎么优秀,然后落到中心思想要钱。这里面相亲的女孩子就是目标类,媒婆就是代理类。要钱就是目标类的方法,夸女方优秀就是在调取目标类之前之后你想做的一些事。
二,代理模式的分类
1.静态代理
静态代理中,我们对目标对象的每个方法都要手动完成,非常不灵活,就比如说接口一旦增加了方法,目标类和代理类都要跟着修改而且每个目标类都要写一个代理类。从JVM层面来说,静态代理就是在编译时就将接口实现类这些都变成了一个个实际的class对象
静态代理实现的步骤
(1)定义一个接口和一个实现类
(2)创建一个代理类实现接口
(3)在代理类中将目标对象注入进来,然后代理类中实现的方法中调用目标类对应的方法。这样的话我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事。接下来上代码:
接口
package com.chen.staticFactory;
//接口
public interface SmsService {
String send(String message);
}
实现类
package com.chen.staticFactory;
public class SmsServiceImpl implements SmsService{
@Override
public String send(String message) {
System.out.println("send message:"+message);
return message;
}
}
代理类
package com.chen.staticFactory;
public class SmsProxy implements SmsService{
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
System.out.println("before method send()");
smsService.send(message);
System.out.println("after method send()");
return null;
}
}
测试类
package com.chen.staticFactory;
public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("java");
}
}
2.动态代理(很重要)!!!
相比于静态代理来说,动态代理更加灵活,我们不需要针对每个目标类都创建一个代理类,并且不需要我们实现接口,我们可以直接代理实现类(CGLIB动态代理机制)
从JVM角度来说,动态代理实在运行时动态生成类字节码,并加载到JVM中
(一)jdk本身的代理
jdk动态代理有两个核心 InvocationHandler接口和Proxy类时核心
Proxy类中使用频率最高的方法是:newProxyInstance() 这个方法是用来生成对象的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
参数解释:
loader:类加载器,用于加载代理对象
interfaces:被代理类实现的一些接口
h 实现了InvocationHandler接口的对象
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
参数解释:
proxy:动态生成的类
method:与代理类对象调用搞得方法相对应
args:当前method方法的参数
你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
实现步骤:
1.创建一个接口和实现类:
2.自定义一个类实现InvocationHandler接口重写invoke方法,并且在invoke方法中调用原生方法
3.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,IncvocationHandler h)方法创建代理对象
上代码:
接口:
package com.chen.proxyFactory;
public interface SmsService {
String send(String message);
}
实现了InvocationHandler的代理类
package com.chen.proxyFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类
*/
public class DebugInvocationHandler implements InvocationHandler {
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before method" + method.getName());
Object result = method.invoke(target, args);
System.out.println("after method" +method.getName());
return result;
}
}
代理对象工厂类(就是生成代理代理对象的)加上测试
package com.chen.proxyFactory;
import java.lang.reflect.Proxy;
public class JdkProxyFactory {
public static Object getProxy(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new DebugInvocationHandler(target));
}
public static void main(String[] args) {
SmsService proxy = (SmsService)JdkProxyFactory.getProxy(new SmsServiceImpl());
proxy.send("java");
}
}
(二)cglib代理
jdk自带的动态代理有一个缺点就是只能代理实现了接口的类,为了解决这个问题我们可以用CGLIB动态代理机制来避免
在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。
就是需要定义一个实现MethodIntercepter的类,重写intercept方法。然后通过Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用德军iu是MehtodIntercepter中的intercept方法
实现步骤
1.定义一个类
2.创建一个实现了MethodIntercepter方法的类并且重写intercept方法(跟jdk动态代理的invoke方法类似)
3.通过Enhancer类的create()创建代理类
上代码:
cglib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
基础类
package com.chen.cgLibTest;
public class AliSmsService {
public String send(String message){
System.out.println("阿里 message:"+message);
return message;
}
}
代理类
package com.chen.cgLibTest;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class DebugMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用之前");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("调用后");
return object;
}
}
获取代理类+测试
package com.chen.cgLibTest;
import net.sf.cglib.proxy.Enhancer;
public class CgLibProcyFactory {
public static Object getProxy(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(clazz.getClassLoader());
enhancer.setSuperclass(clazz);
enhancer.setCallback(new DebugMethodInterceptor());
return enhancer.create();
}
public static void main(String[] args) {
AliSmsService aliSmsService = (AliSmsService) CgLibProcyFactory.getProxy(AliSmsService.class);
aliSmsService.send("haha");
}
}
jdk动态代理和CGLIB动态代理对比
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
- 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。