7、代理模式
代理模式是指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型模式。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般包含三种角色:
抽象主题角色(Subject):抽象主题类的主要职责是声明真实主题与代理的共同接口方法,该类可以是接口也可以是抽象方法。
真实主题角色(RealSubject):该类也被称为被代理类,该类定义了代理所表示的真实对象,是负责执行系统真正的业务逻辑对象。
代理主题角色Proxy:也被称为代理类,其内部有RealSubjuct的引用,因此具备完全的对RealSubject的代理权。客户端调用代理对象的方法,但是会在代理对象前后增加一些处理代码。
代理模式的通用写法:
package com.jdwa.nomalproxy;
public interface ISubject {
void request();
}
package com.jdwa.nomalproxy;
public class RealSubject implements ISubject{
@Override
public void request() {
System.out.println("real service is called ");
}
}
package com.jdwa.nomalproxy;
public class Proxy implements ISubject {
private ISubject subject;
public Proxy(ISubject subject){
this.subject = subject;
}
@Override
public void request() {
before();
subject.request();
after();
}
private void before(){
System.out.println("called before real service ...");
}
private void after(){
System.out.println("called after real service ...");
}
}
package com.jdwa.nomalproxy;
public class Client {
public static void main(String[] args) {
ISubject subject = new RealSubject();
Proxy proxy = new Proxy(subject);
proxy.request();
}
}
从静态代理带动态代理:
package com.jdwa.staticproxy;
public interface IPerson {
void findLove();
}
package com.jdwa.staticproxy;
public class Tom implements IPerson {
@Override
public void findLove() {
System.out.println("must be a beautiful girl");
}
}
package com.jdwa.staticproxy;
public class TomFather implements IPerson {
private IPerson tom;
public TomFather(IPerson tom){
this.tom = tom;
}
@Override
public void findLove() {
before();
tom.findLove();
after();
}
private void before(){
System.out.println("find a suitable girl ...");
}
private void after(){
System.out.println("Get ready to be together ...");
}
}
package com.jdwa.staticproxy;
public class Client {
public static void main(String[] args) {
IPerson tom = new Tom();
IPerson tomFather = new TomFather(tom);
tomFather.findLove();
}
}
动态代理
package com.jdwa.staticproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MatchMarker implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target){
this.target = target;
Class<?> clazz = target.getClass();
return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target,args);
after();
return result;
}
private void before(){
System.out.println("I'm matchmarker,I hava already collect your infomation ,start to look for");
}
private void after(){
System.out.println("They are very satisfied with each other");
}
}
测试
package com.jdwa.staticproxy;
public class Client {
public static void main(String[] args) {
// IPerson tom = new Tom();
// IPerson tomFather = new TomFather(tom);
// tomFather.findLove();
MatchMarker matchMarker = new MatchMarker();
IPerson tom = matchMarker.getInstance(new Tom());
tom.findLove();
}
}
这就是JDK自带的动态代理实现。
我们都知道jdk动态代理采用字节重组,重新生成对象来代替原始对象,以达到动态代理的目的。jdk动态代理生成对象的步骤如下:
a、获取被代理对象的引用,并获取他的所有接口,反射获取。
b、jdk动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
c、动态生成Java代码新加的业务逻辑方法由一定的逻辑代码调用
d、编译新生成的Java代码,得到class
e、重新加载到JVM中运行
我们通过将内存中的对象字节码输出,然后反编译,可以查看代理对象的源代码。
package com.jdwa.staticproxy;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
public class Client {
public static void main(String[] args) throws Exception{
// IPerson tom = new Tom();
// IPerson tomFather = new TomFather(tom);
// tomFather.findLove();
// MatchMarker matchMarker = new MatchMarker();
// IPerson tom = matchMarker.getInstance(new Tom());
// tom.findLove();
IPerson person = new MatchMarker().getInstance(new Tom());
person.findLove();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
fos.write(bytes);
fos.close();
}
}
通过源代码我们可以发现$Proxy0类继承了Proxy类,同时还实现了IPerson接口,重写了findLove方法。在静态块中用反射查找了目标 对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。这些代码,都是jdk帮我们自动生成的。
CGLIB实现
package com.jdwa.staticproxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibMatchMarker implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before(){
System.out.println("cglib ----I'm matchmarker,I hava already collect your infomation ,start to look for");
}
private void after(){
System.out.println("cglib ----They are very satisfied with each other");
}
}
package com.jdwa.staticproxy;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
public class Client {
public static void main(String[] args) throws Exception{
// IPerson tom = new Tom();
// IPerson tomFather = new TomFather(tom);
// tomFather.findLove();
// MatchMarker matchMarker = new MatchMarker();
// IPerson tom = matchMarker.getInstance(new Tom());
// tom.findLove();
// IPerson person = new MatchMarker().getInstance(new Tom());
// person.findLove();
// byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
// FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
// fos.write(bytes);
// fos.close();
Tom tom = (Tom) new CGLibMatchMarker().getInstance(Tom.class);
tom.findLove();
}
}
cglib的执行效率要高于jdk的,就是因为CGLib采用了FastClass机制,他的原理简单来说就是:为代理类和被代理类各生成一个类,这个类会为代理类和被代理类的方法分配一个INDEX(int类型);这个INDEX当作一个入参,FastClass就可以直接定位要调用的方法并直接进行调用,省去了反射调用,所以调用效率比JDK代理通过反射调用高。