上一篇我们学习了静态代理
静态代理有不足:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
例如在上一篇的基础上我们又新增了一个学生模块,代码如下
接口:IStudentDao.java
/**
* 接口
*/
public interface IStudentDao {
void save();
}
目标类:StudentDaoImpl.java
/**
* 接口实现
* 目标类
*/
public class StudentDaoImpl implements IStudentDao {
public void save() {
System.out.println("----已经保存学生数据!----");
}
}
代理类:StudentDaoProxy.java
/**
* 代理类,静态代理
*/
public class StudentDaoProxy implements IStudentDao{
//接收保存目标对象
private IStudentDao target;
public StudentDaoProxy(IStudentDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法
System.out.println("提交事务...");
}
}
测试类:TestProxy.java
/**
* 测试类
*/
public class TestProxy2 {
public static void main(String[] args) {
//目标对象
IStudentDao target = new StudentDaoImpl();
//代理对象,把目标对象传给代理对象,建立代理关系
StudentDaoProxy proxy = new StudentDaoProxy(target);
proxy.save();//执行的是代理的方法
}
}
如果还要有教师类,我们又要写一遍代理类,是不是很烦琐?
ok,救世主动态代理来了!
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(StudentDaoProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
代码示例:
用户接口
package com.hzrj.proxy;
public interface IUserDao {
void save();
}
实现类
package com.hzrj.proxy;
public class UserDaoImpl implements IUserDao{
@Override
public void save() {
System.out.println("----已经保存用户数据!----");
}
}
-----------------------
学生接口
package com.hzrj.proxy;
public interface IStudentDao {
void save();
}
实现类
package com.hzrj.proxy;
public class StudentDaoImpl implements IStudentDao{
@Override
public void save() {
System.out.println("----已经保存学生数据!----");
}
}
--------------------------
动态代理核心类
package com.hzrj.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler<T> implements InvocationHandler {
private T target;
public MyInvocationHandler(T target) {
this.target=target;
}
/**
* proxy:代表动态代理对象
* method:代表正在执行的方法
* args:代表调用目标方法时传入的实参
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" +method.getName() + "方法");
//代理过程中插入监测方法,计算该方法耗时
System.out.println("事务开始...");
Object result = method.invoke(target, args);
System.out.println("事务提交...");
return result;
}
}
---------------------------
测试类
package com.hzrj.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//创建一个实例对象,这个对象是被代理的对象
IUserDao userDao=new UserDaoImpl();
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler myHandle=new MyInvocationHandler<IUserDao>(userDao);
//创建一个代理对象myProxy来代理userDao,代理对象的每个执行方法都会替换执行MyInvocationHandler中的invoke方法
IUserDao userProxy = (IUserDao)Proxy.newProxyInstance(IUserDao.class.getClassLoader(),
new Class<?>[]{IUserDao.class}, myHandle);
//代理执行保存数据的方法
userProxy.save();
}
}
运行结果:
代理执行save方法
事务开始...
----已经保存用户数据!----
事务提交...
测试类2
package com.hzrj.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyTest2 {
public static void main(String[] args) {
//创建一个实例对象,这个对象是被代理的对象
IStudentDao stuDao=new StudentDaoImpl();
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler myHandle=new MyInvocationHandler<IStudentDao>(stuDao);
//创建一个代理对象myProxy来代理userDao,代理对象的每个执行方法都会替换执行MyInvocationHandler中的invoke方法
IStudentDao stuProxy = (IStudentDao)Proxy.newProxyInstance(IStudentDao.class.getClassLoader(),
new Class<?>[]{IStudentDao.class}, myHandle);
//代理执行保存数据的方法
stuProxy.save();
}
}
运行结果:
代理执行save方法
事务开始...
----已经保存学生数据!----
事务提交...
在代理过程中,我们在真正执行被代理对象的方法前后都会加入自己其他处理代码,比方说日志记录,时间记录,事务处理等等。这也是Spring中的AOP实现的主要原理!!!!