代理模式是一种设计模式,它通过为其他对象提供一个代理或占位符来控制对原始对象的访问。即通过代理对象访问目标对象。
代理模式可以在不修改原始类代码的情况下,通过代理类,添加一些功能操作,比如添加方法调用的预处理、后处理等。
代理模式的基本概念包括以下几个部分:
在Java中,代理模式的实现通常可以通过接口实现,但是也可以通过类或者抽象类实现。
JDK动态代理是Java原生支持的代理方式,它允许开发者在运行时动态地创建和使用代理对象。这种方式主要依赖于Java的反射技术。
JDK动态代理的工作原理是在运行时在内存中动态地创建一个接口的实现类。
当我们通过代理类的对象调用方法时,它实际上会调用定义的InvocationHandler
接口的实现类对象的invoke
方法。然后在invoke
方法中我们可以定义预处理、调用目标方法、后处理等,我们也可以选择不调用目标对象的方法。这样,我们就可以在不修改源码的情况下,实现对目标对象的功能增强。
InvocationHandler
和Proxy
JDK动态代理主要涉及到java.lang.reflect
包中的两个类:Proxy
和InvocationHandler
。Proxy类是所有动态代理类的父类,它有一个名为newProxyInstance的方法,这个方法生成动态代理对象。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
InvocationHandler
:它是一个接口,我们必须实现该接口。我们可以通过实现该接口的invoke
方法,定义横切逻辑,然后通过反射机制调用目标类的代码。动态的将横切逻辑和业务逻辑编织在一起。
Proxy
:Proxy
类是所有动态代理类的父类,它有一个名为newProxyInstance
的方法,这个方法生成动态代理对象;newProxyInstance
方法接收三个参数:
ClassLoader loader
(类加载器)、Class<?>[] interfaces
(给代理对象提供的接口)、InvocationHandler h
(调用处理器),并返回一个新的代理对象(对象实现了指定的接口)。
public interface MyInterface {
void doSomething();
}
public class MyInterfaceImpl implements MyInterface {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
InvocationHandler
对象,自定义invoke
方法public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method...");
Object result = method.invoke(target, args);
System.out.println("After method...");
return result;
}
}
Proxy
类生成代理对象,并调用public class Main {
public static void main(String[] args) {
MyInterface myInterface = new MyInterfaceImpl();
InvocationHandler handler = new MyInvocationHandler(myInterface);
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
myInterface.getClass().getClassLoader(),
myInterface.getClass().getInterfaces(),
handler);
proxy.doSomething();
}
}
运行以上代码,得到输出:
Before method...
Doing something...
After method...
AOP是一种编程范式,其目标是提高程序模块化的程度。
在AOP中,切面(Aspect)是包含横切关注点实现的模块。切点(Pointcut)定义了在何处应用切面的代码逻辑。连接点(Joinpoint)是程序执行过程中的某个特定点,如方法调用或异常抛出。通知(Advice)是切面在特定连接点执行的动作。
JDK动态代理可以用于实现AOP中的通知。通过创建目标类的代理对象,我们可以在调用目标方法之前、之后或在抛出异常时插入自定义的逻辑。以下是一个简单的示例:
public class LoggingAspect implements InvocationHandler {
private Object target;
public LoggingAspect(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 使用JDK动态代理创建代理对象
MyService myService = (MyService) Proxy.newProxyInstance(
MyService.class.getClassLoader(),
new Class<?>[]{MyService.class},
new LoggingAspect(new MyServiceImpl()));
// 调用代理对象的方法
myService.doSomething();
动态代理在Spring框架中有广泛的应用,
bean
为代理对象时,Spring会自动为这个bean
创建一个代理对象。当我们调用bean
的方法时,实际上是调用了代理对象的方法。这样,我们就可以在代理对象的方法中添加一些额外的逻辑,例如事务管理、日志记录等。Spring的事务管理
也是通过动态代理实现的。当我们在方法上添加@Transactional
注解时,Spring会为这个方法创建一个代理对象,用于在方法执行前后添加事务管理的代码。在Spring中使用JDK动态代理时,我们需要定义一个切面(Aspect),并在切面中指定切点和通知。以下是一个简单的示例:
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.MyService.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
@After("execution(* com.example.MyService.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}
// 在Spring配置类中启用AOP自动代理
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public LoggingAspect loggingAspect() {
return new LoggingAspect();
}
}
在这个示例中,我们使用了@Aspect
注解来标记LoggingAspect
类作为一个切面。@Before
和@After
注解分别定义了前置通知和后置通知。execution
表达式指定了切点,表示这些通知将应用于com.example.MyService
类的所有方法。通过在Spring配置类中启用AOP自动代理,我们可以确保Spring会为匹配切点的方法创建代理对象,并应用相应的通知。
InvocationHandler
接口和创建代理类即可。对于简单的需求,这种方法是快速且易于实现的。InvocationHandler
接口的方法,这限制了横切逻辑的灵活性和多样性,无法处理复杂的情况。CGLIB(Code Generation Library)是一个开源项目,它提供了在运行时动态生成Java类的能力。CGLIB动态代理的基本原理是通过继承的方式进行代理。当我们对一个类进行CGLIB动态代理时,CGLIB会动态生成一个子类来继承这个类,然后在子类中添加我们的代理逻辑。当我们调用目标方法时,实际上是调用了子类的方法。因此,CGLIB动态代理可以代理任何类,不仅仅是接口。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。