Java 动态代理与AOP
最近被问起AOP和动态代理,这里做一个小总结
代理模式是设计模式中的结构型模式,在不改变原始类的情况下,通过引入代理类来给原始类附加功能.通常用于在侵入代码的情况下,给业务代码附加一些与业务不相关的功能,比如日志,统计信息,鉴权等.
public interface UserDao {
void add();
void update();
}
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("add");
}
@Override
public void update() {
System.out.println("update");
}
}
静态代理
public class UserDaoProxy implements UserDao {
private UserDao userDao;
@Override
public void add() {
System.out.println("pre");
userDao.add();
System.out.println("after");
}
@Override
public void update() {
System.out.println("pre");
userDao.update();
System.out.println("after");
}
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
}
上面我们通过实现接口的方式,实现了UserDao
的代理类,当目标类没有实现接口时,我们可以通过继承的方式进行代理.
使用时,我们直接将实现替换即可,如下:
public static void main(String[] args) {
UserDao userDao = new UserDaoProxy(new UserDaoImpl());
userDao.add();
}
运行结果:
这样我们就在不侵入代码的情况下,完成了业务代码的功能拓展.
但是静态代理有一个缺点,就是我们要针对每一个实现类,都去实现一个它的每一个方法,并且为每个方法添加很多相同的逻辑.并且当我们有多个类需要增强的情况下,还需要实现多个代理类.因此,我们可以使用动态代理解决这个问题.
动态代理
动态代理,就是在代码运行期间,动态的创建代理类,并将原始类进行替换.
Java中我们可以使用反射机制进行实现.
我们实现代理类,实现InvocationHandler
接口即可.如下:
public class MyProxy implements InvocationHandler {
private UserDao userDao;
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
ClassLoader classLoader = MyProxy.class.getClassLoader();
Class<?>[] interfaces = userDao.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader, interfaces, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("pre");
Object invoke = method.invoke(userDao, args);
System.out.println("after");
return invoke;
}
}
首先我们需要自定义一个createProxy
方法,用于生成我们的代理类对象,然后实现invoke
方法,里面是我们的业务逻辑,通过放射机制method.invoke
来执行我们的目标方法.
public static void main(String[] args) {
MyProxy myProxy = new MyProxy();
UserDao userDao = (UserDao) myProxy.createProxy(new UserDaoImpl());
userDao.add();
userDao.update();
}
结果如下:
这样就动态的实现了方法的拓展,只写了一个代理类,一次代理逻辑,以后如果有别的类需要相同的拓展,并且它实现了相同的接口,那么我们就可以使用代理类去创建代理对象,即可实现复用.
如果没有实现接口,那么我们就需要通过继承的方式来实现,JDK默认不支持,我们需要使用CGLIB来支持继承方式的动态代理.
我们现在不实现接口
public class UserDaoImpl{
public void add() {
System.out.println("add");
}
public void update() {
System.out.println("update");
}
}
代理类:
public class MyProxy implements MethodInterceptor {
UserDaoImpl userDao;
public MyProxy(UserDaoImpl userDao) {
this.userDao = userDao;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("pre");
Object invoke = method.invoke(userDao, objects);
System.out.println("after");
return invoke;
}
}
运行:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
UserDaoImpl userDao = new UserDaoImpl();
enhancer.setCallback(new MyProxy(userDao));
enhancer.setSuperclass(UserDaoImpl.class);
UserDaoImpl o = (UserDaoImpl) enhancer.create();
o.add();
o.update();
}
结果:
也可以得到一样的结果.
AOP
Spring的AOP就是通过动态代理来实现的,在目标类实现了接口的情况下,会使用JDK自带的动态代理,如果怕目标类没有实现接口,则会使用CGLIB,如果目标类是final类则会报错,或者方法为static或者final,无法进行动态代理.具体源码的话下次再做总结....