静态代理
抽象主题角色:声明真实主题和代理主题的共同接口。
代理主题角色:代理主题内部含有对真实主题的引用,从而在任何时候操作真实主题对象;代理主题提供一个与真实主题相同的接口,以便在任何时候都可以代替真实主题。代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯的传递调用。
真实主题角色:定义代理角色所代表的的真实对象。
UML图:
抽象主题
public interface Subject { void request(); }
真实主题
public class RealSubject implements Subject { @Override public void request() { System.out.println("真实对象的方法"); } }
代理主题
public class ProxySubject implements Subject { private RealSubject subject; public ProxySubject() { } @Override public void request() { pre(); if (subject == null){ subject = new RealSubject(); } subject.request(); post(); } private void pre(){ System.out.println("方法执行之前"); } private void post(){ System.out.println("方法执行之后"); } }
执行:
public static void main(String[] args) throws Exception { ProxySubject subject = new ProxySubject(); subject.request(); }
输出:
方法执行之前
真实对象的方法
方法执行之后
动态代理
JDK自带的动态代理,实现InvocationHandler接口。
声明接口
public interface MyConnection extends AutoCloseable { void createStatement() throws Exception; @Override void close() throws Exception; }
真实主题
public class MyDefaultConnection implements MyConnection { @Override public void createStatement() throws Exception { System.out.println("Create Statement ..."); } @Override public void close() throws Exception { System.out.println("Close Connection ..."); } }
代理主题
public class MyConnectionProxy implements InvocationHandler { private MyConnection conn; private MyConnection proxyConn; public MyConnectionProxy(MyConnection conn) { this.conn = conn; this.proxyConn = (MyConnection) Proxy.newProxyInstance(MyConnection.class.getClassLoader(), new Class<?>[] {MyConnection.class}, this); } public MyConnection getConn() { return conn; } public MyConnection getProxyConn() { return proxyConn; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println("==代理方法:" + methodName); if("close".equals(methodName)){ System.out.println("**不执行close方法"); }else{ return method.invoke(conn, args); } return null; } }
执行:
public static void main(String[] args) throws Exception { MyConnection connection = new MyDefaultConnection(); MyConnectionProxy proxy = new MyConnectionProxy(connection); proxy.getProxyConn().createStatement(); proxy.getProxyConn().close(); }
你会发现我的代理对象去哪里了?实际上我放在InvocationHandler的实现类里面了,这里参考的是mybatis源码的设计。
输出:
==代理方法:createStatement Create Statement ... ==代理方法:close **不执行close方法
CGLib
CGLib不需要接口就能实现动态代理。
CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>
需要代理的对象
public class Programmer { public void work(){ System.out.println("程序员正在敲代码..."); } public final void finalCannotOverride(){ System.out.println("final方法不能被生成的子类覆盖"); } private void privateCannotOverride(){ System.out.println("private方法不能被生成的子类覆盖"); } }
代理类
public class ProgrammerProxy implements MethodInterceptor { // 真实对象 private Object realObject; // 代理对象 private Object proxyObject; public ProgrammerProxy(Object realObject) { this.realObject = realObject; Enhancer enhancer = new Enhancer(); // 设置需要代理的对象 enhancer.setSuperclass(realObject.getClass()); // 设置代理人 enhancer.setCallback(this); this.proxyObject = enhancer.create(); } public Programmer getProxyObject() { return (Programmer) proxyObject; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { pre(); Object result = method.invoke(realObject, objects); post(); return result; } private void pre(){ System.out.println("==先吃早餐"); } private void post(){ System.out.println("==下班打卡"); } }
执行:
public static void main(String[] args) throws Exception { Programmer programmer = new Programmer(); ProgrammerProxy proxy = new ProgrammerProxy(programmer); proxy.getProxyObject().finalCannotOverride(); proxy.getProxyObject().work(); }
输出:
final方法不能被生成的子类覆盖 ==先吃早餐 程序员正在敲代码... ==下班打卡