代理模式在工作中用的比较多,常见的AOP使用的底层实现就是一种动态代理。
在面试中经常考察设计模式,一般都会问你工作中常用哪几种设计模式,当你说了几种设计模式的时候,一般就会问你什么时候回用到这种设计模式?这个很关键,这个是考察你在工作中是否真正的用到过这种设计模式的一种方式。
1,什么时候用代理模式?
代理模式,简言之,就是用一个代理类,代理当前类。 那为什么要这么做呢? 一般是因为一个类中的方法或者属性不想直接暴露给调用的第三方,也不想重新copy一份代码(修改麻烦,要改两份),所以,一般会用一个代理类,第三方调用的时候直接调用代理类就行了,而代理类中的方法实现还是用的是被代理类中的实现。
2,代理模式的分类
代理模式分为动态代理和静态代理两种,区别就是静态代理的代理关系是代码的编译期就确定的,动态代理是代码的运行期确定的(代码运行起来的时候,程序才知道你想代理哪个类【反射】)。
动态代理又分为cglib代理和jdk的代理,两者的区别是jdk代理只能代理接口,cglib代理既能代理接口又能代理实现类。
3,静态代理。
代理的类图:
subject是接口,proxy是代理类, RealSubject是真实的实现类, Client是调用方,为了说明代理模式的使用场景,我们看一下下面的代码:
interface Subject {
void request();
}
class RealSubject implements Subject {
public void request(){
System.out.println("RealSubject");
}
public void response(){
System.out.println("不想被调用方调用的方法");
}
}
class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
subject.request();
}
}
在上面的一段代码中,面向Client,调用方可以调用request 但是不能调用到response。 这是代理模式最常见的使用场景。
2,jdk动态代理。
动态代理最被广泛的认知就是在spring 的 aop中使用了cglib实现的动态代理, 但是aop不仅仅可以用动态代理实现, aop也可以使用责任链模式实现, 所以读者要搞清楚这里的区别。
jdk代理又一定的局限性, 他要求被代理的类必须有一个接口,但是并不是所有的类都有接口。 下面代码是jdk代理的实现方式:
public interface Service {
//目标方法
public abstract void add();
}
public class UserServiceImpl implements Service {
public void add() {
System.out.println("This is add service");
}
}
2、利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。
class MyInvocatioHandler implements InvocationHandler {
private Object target;
public MyInvocatioHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before-----");
Object result = method.invoke(target, args);
System.out.println("-----end-----");
return result;
}
// 生成代理对象
public Object getProxy() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
}
jdk的动态代理模式写法有一些基本是固定的,必须要实现InvocationHandler接口,实现其中的invoke方法,这里可以看到在调用真实实现类实现方法的时候,前后可以做一些事情,这就是aop的实现,前后可以加日志、事物等等的一些处理。
cglib实现的动态代理:
cglib的动态代理相比jdk的动态代理要灵活很多,可以直接代理实现类。
public class SayHello {
public void say(){
System.out.println("hello everyone");
}
}
public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
//实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
public class DoCGLib {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过生成子类的方式创建代理类
SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
proxyImp.say();
}
}
Cglib实现的关键是通过字节码技术动态创建子类实例。实现spring的MethodInterceptor是为了实现aop。 由于Cglib代理用了动态的创建子类,所以无法为final方法进行代理。