好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.
java 中代理的事项方式主要有:1. 静态代理;2. JDK 动态代理;3. CGLIB 动态代理;4.Spring AOP,现在做一个综合的整理和总结,详细如下:
1. 静态代理
设计思路
在编译时就确定了代理类和被代理类的关系,代理类和被代理类实现相同的接口或继承相同的父类,通过在代理类中调用被代理类的方法来实现间接访问。
源码实现
基于接口方式实现:
interface IUserDao{
void insert();
}
class UserDaoImpl implements IUserDao{
@Override
public void insert() {
System.out.println("这里执行了insert方法......");
}
}
class UserDaoImplProxy implements IUserDao{
private IUserDao userDao;
public UserDaoImplProxy(IUserDao userDao){
this.userDao = userDao;
}
@Override
public void insert() {
System.out.println("代理执行方法前.....");
userDao.insert();
System.out.println("代理执行方法后.....");
}
}
public class StaticProxyWithInterfaceDemo {
public static void main(String[] args) {
IUserDao userDao = new UserDaoImpl();
IUserDao userDaoProxy = new UserDaoImplProxy(userDao);
userDaoProxy.insert();
}
}
优缺点
-
优点:可以代理所有实现接口的类,代理类在编译时就确定,运行时效率高。
-
缺点:灵活性差,被代理的类必须实现接口,且每个被代理类都需要对应一个代理类,代码重复度高,维护成本高。
使用场景
适用于接口明确且稳定,且代理逻辑相对简单固定的场景,如对一组实现了相同接口的类进行统一的日志记录、权限验证等操作。
2. JDK 动态代理
设计思路
基于 Java 内置的反射机制,在运行时动态生成代理类。需要被代理的目标类必须实现一个或多个接口,代理类通过实现InvocationHandler
接口来定义代理行为,然后通过Proxy.newProxyInstance()
方法生成代理对象,该方法会根据指定的类加载器、接口列表和调用处理器(InvocationHandler
实现类)动态生成代理类的字节码并加载到 JVM 中,代理对象会拦截对目标对象接口方法的调用,并将这些调用转发给InvocationHandler
的invoke()
方法处理。
源码实现
interface IStudentDao{
void insert();
}
class StudentDaoImpl implements IStudentDao{
@Override
public void insert() {
System.out.println("执行了insert方法......");
}
}
class ProxyInvoke implements InvocationHandler{
Object target;
public ProxyInvoke(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行方法前......");
method.invoke(target,args);
System.out.println("代理执行方法后......");
return null;
}
}
public class DynamicProxyWithJDKDemo {
public static void main(String[] args) {
IStudentDao studentDao = new StudentDaoImpl();
IStudentDao studentDaoProxy = (IStudentDao) Proxy.newProxyInstance(studentDao.getClass().getClassLoader(),studentDao.getClass().getInterfaces(),new ProxyInvoke(studentDao));
studentDaoProxy.insert();
}
}
优缺点
-
优点:简单易用,无需额外依赖,与 Java 语言高度集成,对接口方法的代理灵活性高,可动态切换代理逻辑,在不修改目标类代码的情况下,能够灵活地为多个不同的目标对象提供统一的代理逻辑。
-
缺点:只能代理实现了接口的类,无法对没有实现接口的类进行代理,且性能相对 CGLIB 动态代理稍低。
使用场景
适用于目标类已经实现了明确的接口,且需要在运行时动态地为多个目标对象添加通用的代理逻辑的场景,如 AOP 中的切面功能实现、RPC 框架中客户端对服务端接口的远程调用代理等。
3. CGLIB 动态代理
设计思路
CGLIB(Code Generation Library)是一个第三方代码生成类库,它通过字节码技术在运行时动态生成目标类的子类来实现代理。当使用 CGLIB 代理时,它会对目标类的字节码进行增强,生成一个继承自目标类的代理类,在代理类的方法中,可以在执行目标方法前后添加自定义的逻辑。CGLIB 主要依赖Enhancer
类和MethodInterceptor
接口来实现动态代理功能。
源码实现
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
class TeacherDao{
public void insert(){
System.out.println("执行了insert方法");
}
}
public class DynamicProxyWithCgLibDemo {
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
TeacherDao teacherProxy = (TeacherDao) Enhancer.create(teacherDao.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("执行方法之前");
proxy.invokeSuper(obj,args);
System.out.println("执行方法之后");
return null;
}
});
teacherProxy.insert();
}
}
优缺点
-
优点:可以代理没有实现接口的类,灵活性强,性能较高,能高效地对类的方法进行拦截和增强,适用于对类的行为进行精细控制的场景。
-
缺点:基于继承的代理方式可能带来问题,如无法代理
final
类和final
方法,可能会破坏被代理类的封装性,且生成的字节码文件较多,可能会影响类加载器的性能。
使用场景
当需要代理的类没有实现任何接口,或者需要对类本身的某些特定行为进行增强时,CGLIB 动态代理是较好的选择。例如,在一些 ORM 框架中,对实体类的持久化操作进行代理增强;在性能敏感的场景中,对一些频繁调用的无接口类的方法进行性能监控等。
综上所述,这三种代理方式各有特点和适用场景,开发人员可以根据实际需求灵活选择合适的代理方式来实现代码的增强和解耦等功能。