快速理解动态代理(从理解到AOP应用) 简单的来说就是我们在不用修改原代码的情况下,可以对一个对象进行扩展和装饰,给他披上新衣。代理的意思可以这样理解,我写好一段对象代码等同于我制造出一只🐔,现在我把这只🐔交给Proxy类,并告诉他这只🐔的信息(种类(类名),会跳什么舞(方法),重量(参数)),代理类proxy得知了这之后,会在使用时内部实现invoke()方法,就可以对这只🐔进行代理操作,比如可以给他穿背带裤,穿滑板鞋,但是这只🐔的内核不会变。
Java中的动态代理是一种技术,允许在运行时创建一个对象,该对象可以实现指定接口的方法。这通常被用来在不修改原始类的情况下,为原始类的方法提供额外的功能。例如,可以使用动态代理为方法添加日志记录功能或者实现访问控制。
要创建动态代理,需要两个主要部分:
1.一个接口,该接口定义了要代理的方法。 2.一个实现了 java.lang.reflect.InvocationHandler 接口的处理器对象,该对象将在调用代理方法时被调用。
Java 语言提供了一种特殊的接口,称为 InvocationHandler,可以用来定义动态代理类的行为。通常,当创建一个动态代理时,你需要提供一个实现了 InvocationHandler 接口的对象,该对象会定义代理类的行为。
同样的代理类也作为一个对象,需要被实例化,: 在 Java 中,动态代理类是通过 java.lang.reflect.Proxy 类来实现的。该类提供了一个静态方法 newProxyInstance,可以用来创建动态代理类的实例。
该方法接受三个参数:
1.一个 ClassLoader 对象,表示用于加载代理类的类加载器。 2.一个 Class 数组,表示要实现的接口。 3.一个 InvocationHandler 对象,表示要使用的处理器对象。
MyInterface.class.getClassLoader(), // 用于加载代理类的类加载器
new Class[] { MyInterface.class }, // 要实现的接口
new MyInvocationHandler(target)); // 要使用的处理器对象
在调用 newProxyInstance 方法时,该方法会根据传入的参数动态地创建一个代理类,并返回该代理类的实例。此后,你就可以通过该实例来调用代理类中定义的方法。
// 定义一个接口
public interface Hello {
void sayHello();
}
// 定义实现该接口的类
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, world!");
}
}
// 定义一个实现了 InvocationHandler 接口的类
public class HelloInvocationHandler implements InvocationHandler {
private Object target;
public HelloInvocationHandler(Object target) {
this.target = target;
}
// 在代理方法被调用时,该方法会被调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用原始方法之前,可以执行额外的操作
System.out.println("Before calling method: " + method.getName());
// 调用原始方法
Object result = method.invoke(target, args);
//同理
System.out.println("After calling method: " + method.getName());
return result;
}
}
// 创建代理类的实例
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] {Hello.class},
new HelloInvocationHandler(new HelloImpl())
);
// 通过代理类的实例调用 sayHello 方法
hello.sayHello();
// 输出:
// Before calling method: sayHello
// Hello, world!
// After calling method: sayHello
在这个例子中,我们定义了一个接口 Hello 和一个实现该接口的类 HelloImpl。然后,我们定义了一个实现了 InvocationHandler 接口的类 HelloInvocationHandler,该类定义了代理类的行为。在这个例子中,我们定义了一个在调用代理类的方法前后都会打印消息的通知。
最后,我们使用 java.lang.reflect.Proxy 类的 newProxyInstance 方法来创建一个代理类的实例
实际应用(提升部分,没学过spring的不用看)
下面是一些动态代理的具体应用场景:
1.AOP(面向切面编程):通过动态代理可以在运行时动态地将切面的逻辑切入到目标方法中,实现对业务逻辑的横切。 2.远程方法调用:通过动态代理可以在运行时创建一个实现了某个远程接口的代理类,并通过该代理类来调用远程服务。 3.事务管理可能会与动态代理配合使用,以实现一些特定的目的。例如,在使用动态代理创建数据访问对象时,可以在代理类中添加事务管理逻辑,以保证数据库操作的正确性。 4对象的虚拟代理,例如用来延迟初始化或者限制对象的访问 5.对象的缓存代理,例如用来缓存网络资源或者数据库查询结果
AOP
那么在 AOP 中,动态代理的工作流程如下: 1。容器在启动时,会扫描应用程序中的切面,并根据切面定义的切点和通知,自动生成一个代理类。 2.当应用程序试图通过代理类的实例调用被拦截的方法时,代理类会拦截这些调用。 3.在拦截方法调用时,代理类会检查该方法是否匹配切面定义的切点,如果匹配,则执行切面中定义的通知。 4.在执行完通知后,代理类会将方法调用委托给原始的目标对象,并返回方法调用
下面是一个简单的例子,展示了如何使用动态代理来实现 AOP:
// 定义一个接口
public interface Hello {
void sayHello();
}
// 定义实现该接口的类
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, world!");
}
}
// 定义切面
@Aspect
public class LoggingAspect {
@Before("execution(void sayHello())")
public void beforeSayHello() {
System.out.println("Before calling sayHello() method");
}
@After("execution(void sayHello())")
public void afterSayHello() {
System.out.println("After calling sayHello() method");
}
}
// 创建代理类的实例
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] {Hello.class},
new LoggingAspect()
);
// 通过代理类的实例调用 sayHello 方法
hello.sayHello();
// 输出:
// Before calling sayHello() method
// Hello, world!
// After calling sayHello() method
在这个例子中,我们定义了一个接口 Hello 和一个实现该接口的类 HelloImpl。
事务管理
下面是一个使用动态代理和事务管理的例子。假设我们有一个数据访问对象,用来从数据库中读取和写入数据。为了保证数据库操作的正确性,我们需要在代理类中添加事务管理逻辑。 首先,我们定义一个接口,表示数据访问对象:
public interface DataAccessObject {
void insert();
void update();
void delete();
}
然后,我们定义一个实现类,用来实现接口中定义的方法:
public class DataAccessObjectImpl implements DataAccessObject {
@Override
public void insert() {
// 插入数据
}
@Override
public void update() {
// 更新数据
}
@Override
public void delete() {
// 删除数据
}
}
接下来,我们使用 JDK 中的 Proxy 类来创建动态代理:
DataAccessObject dao = new DataAccessObjectImpl();
DataAccessObject proxy = (DataAccessObject) Proxy.newProxyInstance(
dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在这里添加事务管理逻辑
return method.invoke(dao, args);
}
}
);
在上面的代码中,我们通过 Proxy.newProxyInstance() 方法创建了一个动态代理类。这个代理类会实现 DataAccessObject 接口,并在每个方法调用前后添加事务管理逻辑。 我们就可以使用这个动态代理类来执行数据库操作:
// 插入数据
proxy.insert();
// 更新数据
proxy.update();
// 删除数据
proxy.delete();
在执行上面的代码时,动态代理类会拦截每个方法的调用,并在调用前后添加事务管理逻辑。这样,我们就可以保证数据库操作的正确性和完整性。
当然,这只是一个简单的例子,实际应用中会更复杂一些。不过,总体思路是一样的:通过动态代理类来拦截方法调用,并在调用前后添加事务管理逻辑。
这里歪一下,简单说说事务管理:
那么接下来我就来为你说明一下如何添加事务管理逻辑。
首先,我们需要确定事务的隔离级别和传播行为。隔离级别用来解决脏读、幻读和不可重复读等问题,常见的隔离级别有:读未提交、读已提交、可重复读和串行化。传播行为则用来控制事务的范围,常见的传播行为有:REQUIRED、REQUIRES_NEW、SUPPORTS、NOT_SUPPORTED 等。
具体的代码实现方式可能会因为使用的框架不同而有所差异。下面是一个简单的例子,使用 Spring 的声明式事务管理来实现事务管理逻辑:
事务管理逻辑
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用前打开事务
transactionManager.begin();
try {
// 执行原来的方法
Object result = method.invoke(dao, args);
// 在调用后提交事务
transactionManager.commit();
return result;
} catch (Exception e) {
// 在出现异常时回滚事务
transactionManager.rollback();
throw e;
}
}