代理模式
Posted huaiheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式相关的知识,希望对你有一定的参考价值。
1 简介
定义:内部含有对真实对象的引用,并由代理对象进行操作控制的运作模式。
目的:对目标方法进行增强。
优势:让增强的动作和目标动作分开,实现解耦,从而保证对原生代码无侵入的目的。
2 详解
代理模式包括静态代理和动态代理两种类型。
2.1 静态代理
其中,静态代理主要是针对某一个类做代理,如下所示:
// 接口
public interface IUserDao {
void save();
void find();
}
//目标对象
class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("模拟:保存用户!");
}
@Override
public void find() {
System.out.println("模拟:查询用户");
}
}
/**
* 静态代理
* 特点:
* 2. 目标对象必须要实现接口
* 2. 代理对象,要实现与目标对象一样的接口
*/
class UserDaoProxy implements IUserDao{
private IUserDao target = new UserDao();
@Override
public void save() {
System.out.println("代理操作: 开启事务...");
target.save();
System.out.println("代理操作:提交事务...");
}
@Override
public void find() {
target.find();
}
}
2.2 动态代理
由于静态代理中的代理类是针对某一个类去做代理的,如果系统中有1000个Interface,那么需要创建1000个代理类,因此静态代理的可复用性不强,而采用动态代理就能很好的解决以上问题。其中,动态代理分为JDK的动态代理和cglib动态代理:
JDK动态代理:利用拦截器加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理。
其中,JDK动态代理使用的前提是目标对象生成了接口,如果目标对象没有实现接口,则必须使用cglib库。JDK的动态代理的使用方式如下所示:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface ISayHelloWorld {
void say();
}
class ManSayHelloWorld implements ISayHelloWorld {
@Override
public void say() {
System.out.println("Hello world!");
}
}
/**
* 这里创建一个拦截器,代理对象的所有方法调用都会做拦截器的invoke方法,从而实现了对原生方法的过滤和增强
* */
class AOPHandle implements InvocationHandler {
/**
* 代理对象
* */
private Object obj;
public AOPHandle(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用前处理
System.out.println("前置代理");
// 调用代理对象的方法并返回ret
Object ret = method.invoke(obj, args);
// 调用结束后处理
System.out.println("后置代理");
// 返回代理对象函数调用的结果
return ret;
}
}
public class Test {
public static void main(String[] args) {
AOPHandle handle = new AOPHandle(new ManSayHelloWorld());
ISayHelloWorld ic = (ISayHelloWorld) Proxy.newProxyInstance(ManSayHelloWorld.class.getClassLoader(), new Class[]{ISayHelloWorld.class}, handle);
ic.say();
}
}
cglib动态代理的使用方式如下所示:
依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
使用:
class Example{
public void test() {
System.out.println("hello,world!");
}
}
/**
* 方法拦截器
* CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象 实现的动态代理
*/
class MethodInvocation implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
//告诉cglib,生成的子类需要继承哪个类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
//生成源代码
//编译成class文件
//加载到JVM中,并返回被代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("------invoke before---------");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("------invoke end---------");
return obj;
}
}
public class Test {
public static void main(String[] args) {
MethodInvocation webApp = new MethodInvocation();
Example boss = (Example) webApp.getInstance(Example.class);
boss.test();
}
}
对比两种动态代理方式:
(1)JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象;
(2)JDK和CGLIb都是在运行期间生成字节码,JDK是直接写Class字节码,CGLib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类的效率比JDK动态代理方式低;
(3)JDK调用代理方法,是通过反射机制调用的,CGLIb是通过FastClass机制直接调用方法,CGLib执行效率更高。
以上是关于代理模式的主要内容,如果未能解决你的问题,请参考以下文章