按照代理的创建时期,代理类可以分为两种:
静态代理:由程序员创建代理类或特定工具自动生成源代码,再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态代理:在程序运行时用反射机制动态创建。
静态代理
代理对象(Proxy)和目标对象(RealSubject)实现了相同的接口(Subject),目标对象作为代理对象的一个属性,具体接口实现中,代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。
(1)Subject接口:
public interface Subject {
public abstract void dealTask();
}
(2)RealSubject类:继承Subject接口,并实现其中的方法
public class RealSubject implements Subject {
@Override
public void dealTask() {
System.out.println("我要吃饭");
}
}
(3)代理类Proxy:继承Subject类,并把Subject作为参数通过构造参数传递。
Subject是个接口,因此在调用的时候传递的应该是他的实现类。
public class Proxy implements Subject {
// 目标对象
private Subject subject;
// 通过构造方法传入目标对象
public Proxy(Subject subject) {
this.subject = subject;
}
@Override
public void dealTask() {
System.out.println("代理来帮你做饭");
subject.dealTask();
System.out.println("代理来帮你刷碗");
}
}
(4)Client客户端调用:
public class Client {
public static void main(String[] args) {
// Proxy类中构造方法传递的是一个接口,在调用的时候就必须传递这个接口的实现类,因此是RealSubject
Subject subject = new Proxy(new RealSubject());
subject.dealTask();
}
}
控制台输出:
代理来帮你做饭
我要吃饭
代理来帮你刷碗
动态代理之JDK
为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
代理模式:
(1)接口
public interface SubjectService {
public void getName(String name);
public void getAge(Integer age);
}
(2)实现类
public class SubjectServiceImpl implements SubjectService{
@Override
public void getName(String name) {
System.out.println("getName方法的实现类:" + name);
}
@Override
public void getAge(Integer age) {
System.out.println("getage方法的实现类"+age);
}
}
(3)代理类:继承InvocationHandler接口并重写invole方法,并把目标类作为参数传递进去
public class MyInvocationHandler implements InvocationHandler {
// 目标方法
private Object target;
public MyInvocationHandler() {
}
// 把目标方法作为参数传递
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 是getName()方法
if ("getName".equals(method.getName())) {
System.out.println("before----" + method.getName());
Object result = method.invoke(target, args);
System.out.println("after----" + method.getName());
return result;
// 是getAge()方法
} else if ("getAge".equals(method.getName())) {
System.out.println("before----" + method.getName());
Object result = method.invoke(target, args);
System.out.println("after----" + method.getName());
return result;
} else {
Object result = method.invoke(target, args);
return result;
}
}
}
(4)客户端
public class Client {
public static void main(String[] args) {
//多态的方式
SubjectService subjectService = new SubjectServiceImpl();
//参数是接口,真正调用传递的就应该是实现类
InvocationHandler invocationHandler = new MyInvocationHandler(subjectService);
//创建代理类
SubjectService serviceproxy = (SubjectService) Proxy.newProxyInstance(
subjectService.getClass().getClassLoader(), subjectService.getClass().getInterfaces(),
invocationHandler);
serviceproxy.getName("小红");
serviceproxy.getAge(20);
}
}
控制台打印:
before----getName
getName方法的实现类:小红
after----getName
before----getAge
getage方法的实现类20
after----getAge
动态代理之CGLIB
cglib需要的jar包有asm.jar、cglib.jar
cglib,code generate library,代理类可对类进行代理,使用第三方cglib库来实现,其内部使用asm框架生成代理类的字节码,其字节码文件更加复杂,不能代理final方法,因为代理类是委托类的子类。
代理对象的生成过程由Enhancer类(CGLib的字节码增强器)实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
(1)定义业务逻辑
public class Subject {
public void say(String name) {
System.out.println("hello " + name);
}
}
(2)实现MethodInterceptor接口,定义方法的拦截器
public class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before----------" + method.getName() + "()");
// 调用MyInterceptor父类的方法,因为在Client类中设置了MyInterceptor的父类是Subject
Object returnObj = methodProxy.invokeSuper(object, args);
System.out.println("after----------" + method.getName() + "()");
return returnObj;
}
}
(3)使用Enhancer类生成代理类
public class Client {
public static void main(String[] args) {
// 利用Enhancer类生成代理类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Subject.class); // 继承被代理类
enhancer.setCallback(new MyInterceptor()); // 设置回调
//采用多态的形式,因为上面通过setSuperclass设置了继承关系
Subject subject = (Subject) enhancer.create(); // 生成代理对象
subject.say("yes");
}
}
控制台输出:
before----------say
hello yes
after----------say
静态代理和动态代理的比较
静态代理的优点:
(1)代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)
静态代理的缺点:
(1)一个代理类只能代理一个业务类。如果业务类增加方法时,相应的代理类也要增加方法。。
(2)如果接口增加一个方法,除了实现类需要实现这个方法外,代理类也需要实现此方法。增加了代码维护的复杂度。
jdk和cglib动态代理实现的区别
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;