动态代理是使用反射和字节码,在运行期间创建指定接口或类的子类以及它的实例对象的一项技术,通过这个技术可以对代码进行无侵入式的增强。java中动态代理技术有两种实现方式:JDK原生动态代理、CGLIB动态代理。
一、JDK原生动态代理
这一种主要是针对有接口实现的情况,它的底层是创建接口的实现代理类,实现扩展功能。也就是我们要增强的这个类实现了某个接口,那么我就可以使用这种方式。这种方式使用到一个类Proxy和一个接口InvocationHandler。
Proxy:所有动态类的父类,它提供一个静态方法来创建动态代理的class对象和实例,这个方法是newProxyInstance()。
InvocationHandler:每个动态代理实例都有一个相关联的InvocationHandler方法,在代理实例调用时,方法调用将被转发到InvocationHandler的invole方法。
代码实现:
User:
public class User {
private String name;
private Integer age;
//getter and setter
}
UserService:
public interface UserService {
public void addUser(User user);
}
UserServiceImpl:
public class UserServiceImpl implements UserService {
@Override
public void addUser(User user) {
System.out.println("新增用户成功,数据为:"+user.toString());
}
}
UserServiceProxy:实现InvocationHandler,重写invoke方法,做前置增强,判断用户年龄是否大于0,如果小于等于0就报运行时异常。
public class UserServiceProxy implements InvocationHandler {
private Object realObj;
public Object getRealObj() {
return realObj;
}
public void setRealObj(Object realObj) {
this.realObj = realObj;
}
public UserServiceProxy(Object realObj) {
super();
this.realObj = realObj;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if(objects!=null && objects.length>0 && objects[0] instanceof User){
User user = (User)objects[0];
if(user.getAge()<=0){
throw new RuntimeException("用户年龄不符合");
}
}
Object ret = method.invoke(realObj,objects);
return ret;
}
}
Client:
public class Client {
public static void main(String[] args) {
User user =new User();
user.setName("张三");
user.setAge(0);
UserService us = new UserServiceImpl();
UserServiceProxy usp = new UserServiceProxy(us);
UserService proxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),usp);
proxy.addUser(user);
}
}
运行结果:
Exception in thread "main" java.lang.RuntimeException: 用户年龄不符合
at DynamicProxy.JDK.UserServiceProxy.invoke(UserServiceProxy.java:30)
at com.sun.proxy.$Proxy0.addUser(Unknown Source)
at DynamicProxy.JDK.Client.main(Client.java:13)
过滤起了作用,将年龄改为大于0的数时,能够正常运行。
静态内部类实现方式:
public class JDKProxy {
@Test
public void fun01(){
AccountDao accountDao = new AccountDaoImpl();
AccountDao proxyDao = (AccountDao) Proxy.newProxyInstance(accountDao.getClass().getClassLoader(), accountDao.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验...");
return method.invoke(accountDao,args);
}
return method.invoke(accountDao, args);
}
});
proxyDao.save();
}
}
二、CGLIB动态代理
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。这是第三方的代理机制,不是jdk自带的. 没有实现接口的类产生代理,使用的是字节码的增强技术,其实就是产生这个类的子类。
CGLIB通过继承方式实现代理:
* Enhancer:来指定要代理的目标对象,实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象的所有非final方法的调用都会转发给MethodInterceptor;
* MethodInterceptor:动态代理对象的方法调用都回转发到intercept方法进行增强。
代码实现:
UserServiceImpl类:无需接口
public class UserServiceImpl {
public void addUser(User user) {
System.out.println("新增用户成功,数据为:"+user.toString());
}
}
UserServiceProxy:
public class UserServiceProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects,MethodProxy proxy) throws Throwable {
if(objects!=null && objects.length>0 && objects[0] instanceof User){
User user = (User)objects[0];
if(user.getAge()<=0){
throw new RuntimeException("用户年龄不符合");
}
}
Object ret = proxy.invokeSuper(realObj,objects);
return ret;
}
}
Client:
public class Client {
public static void main(String[] args) {
User user =new User();
user.setName("张三");
user.setAge(0);
Enhancer enhancer = new EnHancer();
enhancer.setSuperClass(UserServiceImpl.class);
enhancer.setCallback(new UserServiceProxy());
UserServiceImpl usi = (UserServiceImpl) enhancer.create();
usi.addUser(user);
}
}
静态内部类实现方式:
public class C_CglibProxy {
@Test
public void fun01(){
AccountDaoImpl accountDao = new AccountDaoImpl();
//创建enhancer对象
Enhancer enhancer = new Enhancer();
//设置代理的父类
enhancer.setSuperclass(AccountDaoImpl.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验...");
return method.invoke(accountDao, args);
}
return method.invoke(accountDao, args);
}
});
AccountDaoImpl accountDaoProxy = (AccountDaoImpl) enhancer.create();
accountDaoProxy.save();
}
}
三、总结
- java的动态代理技术的实现主要有两种方式:
* JDK原生动态代理
* CGLIB动态代理 - JDK原生动态代理是Java原生支持的,不需要任何外部依赖,但是它只能基于接口进行代理(需要代理的对象必须实现于某个接口);
- CGLIB通过继承的方式进行代理(让需要代理的类成为Enhancer的父类),无论目标对象有没有实现接口都可以代理,但是无法处理final的情况。