1、代理
代理,简称为Proxy。意思就是你什么都不用去做,别人代替你去做。
她在程序开发中起到了非常重要的作用,比如AOP,就是针对代理的一种应用。此外,在设计模式中,还有一个“代理模式”。
接下来,通过一个例子,由浅及深学习
public interface Hello{
void say(String name)
}
上面是一个Hello的接口,接下来是实现类
public class HelloImpl implements Hello{
@Override
public void say(String name){
System.out.println("Hello!"+name);
}
}
如果要在println方法前面和后面分别需要处理一些逻辑,如果把这些逻辑写进say方法了,肯定是不行的。
那么此时,就需要用代理模式,写一个HelloProxy类,让他去调用HelloImpl的say方法,在调用前后分别进行逻辑处理。具体如下:
public class HelloProxy implements Hello{
private Hello hello;
public HelloProxy(){
hello=new HelloImpl();
}
@Override
public void say(String name){
before();
hello.say(name);
after();
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
用HelloProxy类实现了Hello接口,并且哎构方法中new出一个HelloImpl类的实例。这样,我们就可以在HelloProxy的say方法里面调用HelloImpl的say方法了。更重要的是,我们在调用前加上了before和after方法,在这两个方法实现那些前后逻辑。
用一个main方法测试一下:
public static void main(String[] args){
Hello helloProxy=new HelloProxy();
helloProxy.say("Jack");
}
打印的结果为:
Before
Hello!Jack
After
这样,我们就写了一个HelloProxy的“代理模式”。
2、JDK动态代理
上述中,使用的就是静态代理,那么什么是动态代理呢?
下面用JDK提供额动态代理方案写一个DynamicProxy:
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
before();
Object result=method.invoke(target,args);
after();
return result;
}
.....
}
在DynamicProxy类中,定义了一个Object类型的target变量他就是被代理的目标对象,通过构造函数来初始化,也就是注入。构造方法称为反射。
在DynamicProxy类中,实现了 InvocationHandler接口 ,那么必须实现该接口的invoke方法。该方法是jar“反射”过来的。在invoke中,在调用前后分别处理before和after,左后result返回。
public static void main(String[] args){
Hello hello=new HelloImpl();
DynamicProxy dynamicProxy=new DynamicProxy(hello);
Hello helloProxy=(Hello)Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxy
);
helloProxy.say("Jack");
}
意思就是通过用这个通用的DynamicProxy 类去包装HelloImpl实例,然后再调用JDK给我们提供的Proxy类的工厂方法newProxyInstance去动态创建一个Hello接口的代理类,最后调用这个代理类的say方法。
运行的结果是一样的,动态代理成功了。那么,动态代理就是帮我们自动生成XxxProxy类的法宝。
但是,Proxy.newProxyInstance方法参数有点令人烦躁
- 参数1:ClassLoader
- 参数2:该实现类的所有接口
参数3:动态代理对象
调用完后,还需要强制转换一下。
如果需要的代理对象比较多,那么意味着,到处都出现Proxy.newProxyInstance方法的情况。所有需要把DynamicProxy 重构一下:
public class DynamicProxy implements InvocationHandler{
@SuppressWarnings("unchecked")
public <T> T getProxy(){
return (T)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target..getClass().getInterfaces(),
this
);
}
.....
}
在DynamicProxy 里添加了一个getProxy方法,无须传入任何参数,将刚才说的那一块代码放入这个方法中,并且该方法返回一个泛型类型就不会强制转换类型。方法上添加一个@SuppressWarnings(“unchecked”)注解表示忽略编译时的警告(因为Proxy.newProxyInstance方法返回的是一个Object,这里强制转换为T了,这是向下转型。)
那么此时使用DynamicProxy 就简单了。
public static void main(String[] args){
DynamicProxy dynamicProxy=new DynamicProxy(new HelloImpl());
Hello helloProxy=dynamicProxy.getProxy();
helloProxy.say("Jack");
}
此时,代码简洁多了,那么动态代理大概也就懂了。
3、CGlib动态代理
动态代理并不是万能的,总有用不了的地方。那么在Spring、Hibernate中的高端框架中都有用到过CGlib类库。它是一个在运行期间动态生成字节码的工具,也就是动态生成代理类。
下面通过代码简洁的说下,我理解的也不深~
public class CGlibProxy implements MethodInterceptor{
public <T> T getProxy(Class<T> cls){
return(T)Enhancer.create(cls,this);
}
public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{
before();
Object result=proxy.invokeSuper(obj,args);
after();
result result;
}
...
}
需要实现CGlib给我们提供的MethodInterceptor实现类,并填充intercept方法。方法中最后一个MethodProxy类型的参数proxy值得注意。Cglib给我们提供的是方法级别的代理,也可以理解为对方法的拦截(这就是“方法拦截器”)。我们直接调用proxy的invokeSuper方法,将被代理的对象obj以及方法参数args传入其中即可。
与DynamicProxy 类似,在CGlib中也添加一个泛型的getProxy方法,便于我们快速地获取自动生成的代理对象。
public static void main(String[] args){
CGlibProxy cglibProxy=new CGlibProxy();
Hello helloProxy=cglibProxy.getProxy(HelloImpl.class);
helloProxy.say("Jack");
}
对于此刻的代码,如果用“单例模式”后,会不会更加简洁些呢。
public class CGlibProxy implements MethodInterceptor{
private static CGlibProxy instance=new CGlibProxy();
private CGlibProxy(){
}
public static CGlibProxy getInstance(){
return instance;
}
...
}
那么此时,main中:
public static void main(String[] args){
Hello helloProxy=CGlibProxy.getInstance.getProxy(HelloImpl.class);
helloProxy.say("Jack");
}
以上,简述了静态代理和动态代理,如果有更好的见解,欢迎指点~