一、实现方式有哪些?
1.基于接口实现:JDK动态代理
2.基于类实现:cglib方式
3.基于java字节码方式:javasist
二、JDK动态代理实现
一、定义一个接口 UserService
public interface UserService {
void add();
void delete();
void update();
void select();
}
二、添加实现
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void select() {
System.out.println("查询了一个用户");
}
}
三、测试接口方法
public class ProxyTest01 {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.add();
userService.delete();
userService.update();
userService.select();
}
}
假设现在有一个需求:在每个方法执行前后打印调用时间。
现在能想到的办法就是:
1.直接修改源代码(造成代码冗余)
2.用静态代理(代码复用性低)
3.动态代理(相对较好)
四、如何用动态代理实现该需求?
编写一个可以创建代理的类,实现InvocationHandler接口
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* 该方法用户动态获取代理对象
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//通过反射机制调用对象的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
start(method.getName());
Object result = method.invoke(target, args);
end(method.getName());
return result;
}
private void start(String msg){
System.out.println("开始执行" + msg + "方法" + new Date().toString());
}
private void end(String msg){
System.out.println("结束执行" + msg + "方法" + new Date().toString());
}
}
五、创建测试类
public class Client {
public static void main(String[] args) {
//被代理的类
UserServiceImpl userService = new UserServiceImpl();
//设置动态代理
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(userService);
//通过反射机制获取动态代理的对象并强转
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
//调用动态代理对象的方法
proxy.add();
proxy.update();
proxy.delete();
proxy.select();
}
}
输出
通过动态代理实现了该需求。
//通过反射机制调用对象的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
start(method.getName());
Object result = method.invoke(target, args);
end(method.getName());
return result;
}
private void start(String msg){
System.out.println("开始执行" + msg + "方法" + new Date().toString());
}
private void end(String msg){
System.out.println("结束执行" + msg + "方法" + new Date().toString());
}
在执行代理对象的add、delete、update、select方式时,都是通过反射机制将方法体,实参传入invoke方法来执行,result就是实际的方法返回值,例如将add方法修改一下
//将接口与实现的add方法返回值修改为Integer类型
@Override
public Integer add() {
System.out.println("增加了一个用户");
return 1;
}
但是我们可以在invoke方法中讲返回值进行二次处理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
start(method.getName());
Object result = method.invoke(target, args);
end(method.getName());
result = 10;
return result;
}
例如我们将返回值重新赋值为10
再次测试
Integer add = proxy.add();
System.out.println("add方法的返回值" + add);
proxy.update();
proxy.delete();
proxy.select();
结果:
发现返回值已经被修改。