Java动态代理的2种实现方式
- Jdk动态代理
- CGLIB动态代理
JDK动态代理
JDK动态代理主要是通过java.lang.reflect.Proxy类实现的,主要是通过该类的newProxyInstance方法来创建目标类的代理对象,从而实现对目标类的功能增强。在此之前你必须创建一个实现了InvocationHandler接口的代理类,并实现它的方法invoke(),在该方法中完成对目标类的功能增强。
- step1
创建需要被代理的类(即需要进行功能增强的类)
/**
* 接口类
*/
public interface UserDao {
public void addUser();
public void delete();
}
/**
* 接口实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public void addUser() {
System.out.println("添加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
public void edit(){
System.out.println("编辑用户");
}
}
- step2
创建切面类(用于进行业务功能增强的类)例如:日志记录,用户权限检查等…
public class Aspect {
public void checkPermission(){
System.out.println("检查用户权限...");
}
public void recordLog(){
System.out.println("日志记录...");
}
}
- step3
创建JDK代理类
import com.study.aspect.Aspect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 创建jdk代理类 需要实现InvocationHandler接口
*/
public class JdkProxy implements InvocationHandler {
private Object target;
/**
* 创建目标对象的代理对象
* @param target
*/
public Object creatProxy(Object target){
this.target = target;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
/**
* @param proxy 目标类的代理对象
* @param method 目标类的方法
* @param args 目标类的方法参数
* @return 目标类的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//创建切面类,即需要进行功能增强的类
Aspect aspect = new Aspect();
//目标类方法调用之前调用功能增强
aspect.checkPermission();
//调用被代理对象的方法 即:UserDao目标类里的方法
Object result = method.invoke(target, args);
//目标类方法调用之后调用功能增强
aspect.recordLog();
return result;
}
}
- step4
JDK代理测试
public class AppTest{
@Test
public void testJdkProxy(){
//创建目标对象
UserDao userDao = new UserDaoImpl();
//创建代理对象实例并获取增强后的userDao的代理对象
UserDao proxy = (UserDao) new JdkProxy().creatProxy(userDao);
// 通过代理对象调用方法
// proxy对象调用的方法会交给JdkProxy类的invoke()方法处理
proxy.addUser();
System.out.println("-------------");
proxy.delete();
}
}
运行结果:
总结:
- JDK动态代理的目标类必须继承一个或多个接口,即UserDaoImpl继承自UserDao
- 所有生成的动态代理的对象在调用方法时都会交由invoke() 方法处理,在该方法中进行增强处理
CGLIB动态代理
CGLIB(Code Generation Libary) 是一个高性能的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对其子类进行增强。Enhancer类时CGLIB的核心类,通过该类create()方法创建代理对象。在此之前你必须创建一个实现了MethdoInterceptor接口的代理类,并实现它的方法intercept(),在该方法中完成对目标类的功能增强。
- step1
引出相关的jar包cglib-3.2.10.jar
或者 通过maven引入
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
- step2
创建需要被代理的类(即需要进行功能增强的类)
public class UserDao2 {
public void addUser() {
System.out.println("添加用户");
}
public void delete() {
System.out.println("删除用户");
}
public void edit(){
System.out.println("编辑用户");
}
}
-
step3
创建切面类 (使用上面JDK动态代理step2的切面类) -
step4
创建CGLIB动态代理类
import com.study.aspect.Aspect;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//继承MethodInterceptor接口并实现intercept()方法
public class CGLIBProxy implements MethodInterceptor {
public Object createProxy(Object target){
//创建一个动态类的对象 Enhancer是CGLIB的核心类通过它来创建代理对象
Enhancer enhancer = new Enhancer();
//设置父类,即设置需要进行功能增强的类
enhancer.setSuperclass(target.getClass());
//设置回调方法
enhancer.setCallback(this);
//创建代理类并返回
return enhancer.create();
}
/**
* @param proxy 目标类的代理对象
* @param method 目标类的方法
* @param args 目标类方法所需要的参数
* @param methodProxy 目标类方法的代理方法对象
* @return 目标类的返回值
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//创建切面类,即需要进行功能增强的类
Aspect aspect = new Aspect();
//目标类方法调用之前调用功能增强
aspect.checkPermission();
//调用被代理对象的方法 即:UserDao目标类里的方法
Object result = methodProxy.invokeSuper(proxy,args);
//目标类方法调用之后调用功能增强
aspect.recordLog();
return result;
}
}
- step5
CGLIB代理测试
public class AppTest{
@Test
public void testCGLIBProxy(){
//创建目标对象
UserDao2 userDao = new UserDao2();
//创建代理对象实例并获取增强后的userDao的代理对象
UserDao2 proxy = (UserDao2) new CGLIBProxy().createProxy(userDao);
// 通过代理对象调用方法
// proxy对象调用的方法会交给CGLIBProxy类的intercept()方法处理
proxy.addUser();
System.out.println("************");
proxy.delete();
}
}
运行结果:
总结:
- CGLIB的实现原理是通过创建目标类的子类,并对子类完成增强功能
- CGLIB的实现目标类无需实现任何接口
- 任何代理对象调用的方法都会交给MethodInterceptor的方法intercept() 去处理
注意:
1.JDK动态代理中JdkProxy类中的invoke()方法中的Object result = method.invoke(target, args); 该段代码中需要传入target(目标类对象),这的target是目标类的对象,target不可换成proxy(目标类的代理对象),否则就相当于调用代理对象的方法,而不是target对象的方法。因为只有调用proxy的方法就会调用JdkProxy类中的invoke()方法,然后Object result = method.invoke(proxy, args);的调用,又会引起JdkProxy类中的invoke() 方法,如此会造成无限循环调用,造成栈溢出异常,使程序崩溃。
2.同样,CGLIB中CGLIBProxy类中的intercept() 方法中的Object result = methodProxy.invokeSuper(proxy,args);该段代码也不能换成methodProxy.invoke(proxy,args) 和上面是同样的道理。invokeSuper() 方法是调用proxy父类的方法,因为CGLIB是对目标类创建子类完成增强,所以proxy的父类就是目标类,使用invokeSuper() 方法不会有问题。
本篇是我对java动态代理学习的笔记,欢迎各位大哥大姐批评指点。