动态代理:使用反射和字节码的技术,在运行期间创建指定接口或类的子类(动态代理)以及其实例对象的技术,通过这个技术可以无侵入性的为代码进行增强。
- JDK动态代理
它有两个重要的伙伴
- Proxy:Proxy是所有动态代理的父类,它提供了一个静态方法来创建动态代理的class对象和实例
- InvocationHandler:每个动态代理实例都有一个关联的InvocationHandler。在代理实例上调用方法时,方法调用将被转发到InvocationHandler的invoke方法
接口及实现类
public interface UserService {
void addUser(String userName);
}
public class UserServiceImpl implements UserService {
@Override
public void addUser(String userName) {
System.out.println("接收到user姓名" + userName);
}
}
代理类
public class UserServiceInterceptor implements InvocationHandler {
private Object realObject;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法运行前");
if (args != null && args.length > 0) {
String str = (String) args[0];
if ("aa".equals(str)) {
throw new RuntimeException("格式错误");
}
}
Object invoke = method.invoke(realObject, args);
System.out.println("方法运行结束");
return invoke;
}
public Object getRealObject() {
return realObject;
}
public void setRealObject(Object realObject) {
this.realObject = realObject;
}
public UserServiceInterceptor(Object realObject) {
this.realObject = realObject;
}
}
运行类
public class UserMain {
public static void main(String[] args) {
UserService us = new UserServiceImpl();
String userName = "张强";
UserServiceInterceptor usi = new UserServiceInterceptor(us);
UserService userProxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(), us.getClass().getInterfaces(), usi);
userProxy.addUser(userName);
}
}
- CGLIB代理
也有两个重要的东西
- Enhancer:指定要代理的目标对象、实际处理代理逻辑的对象,最终调用create()方法得到代理对象,对这个对象的非final方法的调用都会发送给MethodInterceptor。
- MethodInterceptor:动态代理对象的方法调用都会发送给intercept方法进行增强。
被代理类
public class UserServiceImpl {
public void addUser(String userName) {
System.out.println("接收到user姓名" + userName);
}
}
代理类
public class UserServiceInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("方法运行前");
if (args != null && args.length > 0) {
String str = (String) args[0];
if ("aa".equals(str)) {
throw new RuntimeException("格式错误");
}
}
Object invoke = methodProxy.invokeSuper(o, args);
System.out.println("方法运行结束");
return invoke;
}
}
运行类
public class UserMain {
public static void main(String[] args) {
String userName = "张强";
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new UserServiceInterceptor());
enhancer.setSuperclass(UserServiceImpl.class);
UserServiceImpl userServiceProxy = (UserServiceImpl) enhancer.create();
userServiceProxy.addUser(userName);
}
}
- 总结
- jdk动态代理是Java原生支持,不需要外部依赖,但是它只能基于接口进行代理。
- CGLIB通过继承的方式进行代理,无论目标有没有实现接口都可以代理,但是无法处理final的情况。
- CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多。因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。