Java代理模式小结
问题提出
如果由一个类User,其中有一个方法add(),如下
public class User {
public void add() {
System.out.println("add...");
}
}
如果要对add()方法扩展功能,比如在输出"add..."之前,先输出一句"before...",在输出"add..."之后输出一句"after...",那么就需要修改add()方法,如下
public class User {
public void add() {
System.out.println("before...");
System.out.println("add...");
System.out.println("after...");
}
}
但是直接对add()方法进行修改,不利于系统维护,也容易产生代码的重复。比如在add()以及许多方法中,需要添加记录日志的功能,则需要在这些方法中写记录日志功能的逻辑,或者调用记录日志功能的方法,这会产生很多重复的代码,不利于模块的重用。
问题的解决方案
为了解决如上问题,最早使用的方案是纵向继承机制
纵向继承机制
纵向继承机制即继承于一个类,从而在方法中调用父类的方法,实现功能扩展,如下
public class BaseUser {
//记录日志的方法
public void writeLog() {
...
}
}
public class User extends BaseUser {
public void add() {
...
//调用父类方法,实现功能扩展
super.writeLog();
}
}
但是这种做法也有弊端,并不能根本上解决问题,比如对BaseUser类中的writeLog()方法进行修改,将其改名位recordLog()或添加参数,则User类中的add()方法也要进行修改。这样也是不利于维护的。
横向抽取机制(代理模式)
为了从根本上解决问题,出现了代理模式,代理模式即为委托类创建代理类,通过代理类可以调用委托类的部分功能,并添加一些额外的业务处理。代理模式分为静态代理和动态代理
-
静态代理: 自己手动创建代理类,代理类在编译期间就已经确定
public interface Dao { public void add(); } //委托类 public class DaoImpl implements Dao { public void add() { System.out.println("add..."); } } //代理类 public class DaoProxy implements Dao { private DaoImpl daoImpl; public DaoProxy(DaoImpl daoImpl) { this.daoImpl = dapImpl; } public void add() { System.out.println("beagin"); daoImpl.add(); System.out.println("after"); } } //测试类 public class ProxyTest { public static void main(String[] args) { DaoImpl daoImpl = new DaoImpl(); DaoProxy daoProxy = new DaoProxy(daoImpl); daoProxy.add(); } } -
动态代理: 代理类不是由自己编写,而是在运行时期生成,下面详细介绍动态代理
动态代理
动态代理分为jdk动态代理和cglib动态代理,Spring的AOP动态代理就是通过这两者实现的
jdk动态代理
jdk动态代理即使用jdk中提供反射类Proxy和回调接口InvocationHandler实现jdk动态代理,并要求委托类必须实现至少一个接口
实现步骤:
-
定义业务逻辑
public interface Dao { public void add(); } public class DaoImpl implements Dao { public void add() { System.out.println("add..."); } } -
利用反射类Proxy和回调接口InvocationHandler实现jdk动态代理
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; class TestInvocationHandler implements InvocationHandler { private Object target; public TestInvocationHandler(Object target) { this.target = target; } //实现InvocationHandler接口的invoke方法,用于执行目标对象 @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("after..."); return result; } //生成代理对象 public Object getProxy() { //定义代理类的加载者 ClassLoader loader = Thread.currentThread().getContextClassLoader(); //被代理对象实现的接口列表 Class<?>[] interfaces = target.getClass().getInterfaces(); //使用Java字节码技术生成代理对象并返回 return Proxy.newProxyInstance(loader, interfaces, this); } } -
测试类
public class ProxyTest { public static void main(String[] args) { Dao dao = new DaoImpl(); TestInvocationHandler handler = new TestInvocationHandler(dao); //daoProxy就是生成的代理对象,它继承于Proxy类,且实现了Dao接口 Dao daoProxy = (Dao) handler.getProxy(); //执行add()方法,实际上是执行handler对象的invoke方法,传入的参数分别为当前代理对象,当前执行方法和方法参数 daoProxy.add(); } }
cglib动态代理
jdk动态代理需要实现类通过接口定义业务方法,对于没有接口的类,jdk毫无办法,这就需要cglib了。
- cglib是一个强大、高性能、高质量的Code生成类库,它封装了asm(Java字节码操控框架)
- cglib通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
- 由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理
实现步骤:
-
需要被代理的类
public class Dao { public void add() { System.out.println("add..."); } } -
方法拦截器
import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; //方法拦截器 public class TestProxy implements MethodInterceptor { //增强原有方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //前置代理 System.out.println("before..."); Object result = proxy.invokeSuper(obj, args); //调用父类方法 //后置代理 System.out.println("end..."); return result; } } -
产生Dao代理类的工厂类
import net.sf.cglib.proxy.Enhancer; public class DaoFactory { private static Dao dao; static { dao = new Dao(); } public static Dao getDao() { return dao; } //获取代理类 public static Dao getProxyInstance(TestProxy proxy) { Enhancer en = new Enhancer(); en.setSuperclass(Dao.class); en.setCallback(proxy); return (Dao) en.create(); } } -
测试
public class Test { public static void main(String[] args) { Dao dao = DaoFactory.getProxyInstance(new TestProxy()); dao.add(); } }
Spring中AOP的代理方式
-
如果目标对象实现了接口,默认情况下会采用jdk动态代理实现AOP
-
如果目标对象实现了接口,可以强制使用cglib动态代理实现AOP
-
强制使用cglib实现方式:
-
添加cglib库
-
在Spring配置文件中加入
<aop:aspectj-autoproxy proxy-target-class="true"/>
-
-
-
如果目标对象没有实现接口,AOP必须由cglib动态代理实现
-
Spring会自动在jdk动态代理和cglib动态代理之间切换

浙公网安备 33010602011771号