AOP基础-JDK动态代理

时间:2023-12-28 13:20:14

  动态代理技术就是用来产生一个目标对象的代理对象的,代理对象应与目标对象(被代理的对象)有相同的方法,实现对目标对象访问的拦截,并增强目标对象的一些功能,而不需要目标对象去做任何的更改,使得目标对象有更加纯粹的业务逻辑。不多解释,上代码。

  需求:在一个业务模块方法中添加日志。

一、常规的写法

  1.1、接口Animals

package com.duchong.proxy_test;

/**
* @author DUCHONG
* @since 2017-12-28 9:10
**/
public interface Animals {
/**
* 吃
* @param name
* @return
*/
String eat(String name); /**
* 吠
* @param name
* @return
*/
String bark(String name); /**
* 跑
* @param name
* @return
*/
String move(String name);
}

  1.2、接口的实现类Dog

package com.duchong.proxy_test;

/**
* @author DUCHONG
* @since 2017-12-28 9:21
**/
public class Dog implements Animals { @Override
public String eat(String name) {
System.out.println("Dog eat method start.....");
return "dog eat "+name;
} @Override
public String move(String name) {
System.out.println("Dog move method start.....");
return "dog move "+name;
} @Override
public String bark(String name) {
System.out.println("Dog bark method start.....");
return "dog bark "+name;
} public static void main(String[] args) {
Dog dog=new Dog();
System.out.println(dog.eat("Bone"));
System.out.println(dog.move("Faster"));
System.out.println(dog.bark("Wang Wang Wang"));
}
}

  1.3、输出

AOP基础-JDK动态代理

以上写法,能实现要求,但是缺点是,像日志这种非业务逻辑相关的代码混在里面,看起来混乱,不易维护,而且这些代码块大多类似,对于这样的代码块,我们都知道,是可以抽取出来的,定义公共功能,这样就使得业务模块更简洁, 只包含核心业务代码。

二、基于动态代理的写法

2.1、接口的实现类Dog

package com.duchong.proxy_test;

/**
* @author DUCHONG
* @since 2017-12-28 9:21
**/
public class Dog implements Animals {
@Override
public String eat(String name) {
return "dog eat "+name;
} @Override
public String move(String name) {
return "dog move "+name;
} @Override
public String bark(String name) {
return "dog bark "+name;
} }

2.2、使用java.lang.reflect.Proxy动态代理实现,即提取目标对象的接口(或者说JDK生成代理必须使用接口),然后对接口创建代理.

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException

使用Proxy.newProxyInstance方法就可以返回一个代理对象,这个方法总共有3个参数

  • ClassLoader loader 生成代理对象使用哪个类装载器加载
  • Class<?>[] interfaces 生成哪个对象的代理对象,通过接口指定,或者说代理要实现的接口
  • InvocationHandler h 产生的这个代理对象要做什么,这个接口里面只有一个方法,可以使用一个匿名内部类来实现

2.3、代理类

package com.duchong.proxy_test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* dog代理类
*
* @author DUCHONG
* @since 2017-12-28 10:17
**/
public class DogProxy{ //需要被代理的目标对象
private Animals target; public DogProxy(Animals target){
this.target=target;
} /**
* 返回基于接口创建的代理
* @return
*/
public Animals getDogProxy(){ return (Animals) Proxy.newProxyInstance(DogProxy.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("eat")){
System.out.println("Dog eat method start.....");
return method.invoke(target,args);
}
else if(method.getName().equals("move")){
System.out.println("Dog move method start.....");
return method.invoke(target,args);
}
else if(method.getName().equals("bark")){
System.out.println("Dog bark method start.....");
return method.invoke(target,args);
}
else{
return null;
}
}
}); } }

2.4、Main运行

package com.duchong.proxy_test;
/**
* @author DUCHONG
* @since 2017-12-28 10:35
**/
public class Main {
public static void main(String[] args) {
//目标对象
Animals animals=new Dog();
//获得代理对象
Animals dog= new DogProxy(animals).getDogProxy();
//调用代理对象的方法
System.out.println(dog.eat("Bone"));
System.out.println(dog.move("Faster"));
System.out.println(dog.bark("Wang Wang Wang"));
}
}

2.5、输出

AOP基础-JDK动态代理

2.6、InvocationHandler原理

  如果Proxy生成代理对象的时候,指定了InvocationHandler,那么用户调用代理对象的任何方法,该方法都是调用InvocationHandler的invoke方法,而Method就是当前调用的那个方法,通过getName 即可获取当前被调用方法的名称,从而在invoke被调用之前,添加日志等处理,而invoke方法被调用是需要两个参数,一个是调用改方法的对象,一个是该方法的参数,即为代码中的target和args.