实现动态代理
根据网络上很多资料,实现一个业务接口的动态代理只需要三步:
- 定义业务接口
- 定义实现业务接口的业务类
- 根据Proxy类创建任何接口的代理类
第一:定义业务接口
AnimalInterface.java
package proxy.imp;
/**
* 动态代理的业务接口定义
*
* @ClassName: AnimalInterface
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午3:52:15
*
*/
public interface AnimalInterface {
// 设置名字
void setName(String name);
// 获取名字
String getName();
// 叫声
void say();
// 获取栖性
void getProperty();
// 设置栖性
void setProperty(String Property);
}
第二:定义一个实现业务接口的具体类,也叫委托类,等下被代理的类
DogImp.java
package proxy;
import proxy.annon.Seven;
import proxy.imp.AnimalInterface;
/**
* 实现接口的具体业务类
*
* @ClassName: DogImp
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午3:53:12
*
*/
public class DogImp implements AnimalInterface {
@Seven(value = "Lumia")
private String name;
private String Property;
public DogImp() {
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void say() {
System.out.println("小狗:汪汪汪汪.....");
}
@Override
@Seven(Property = "水陆两栖战士")
public void setProperty(String Property) {
this.Property = Property;
}
@Override
public void getProperty() {
System.out.println(this.name + "= " + this.Property);
}
}
可以看到我这里使用了注解给具体业务类属性赋值的技术,所以就引入了注解的定义和解析;详情可看下面
第三步:先要实现一个调用处理器,然后Proxy类动态生成代理类。
实现调用处理器
AOPHandle.java
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import proxy.imp.AOPMethod;
/*
* 调用处理器,每一个代理类都必须有一个关联的调用处理器,当代理类上的一个方法被调用都会被分发到这个调用处理器上
* 的invoke方法上面; InvocationHandler接口被实现可以作为代理类的调用处理器功能
* @ClassName: AOPHandle
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午3:55:29
*
*/
public class AOPHandle implements InvocationHandler {
// 保存AOP切入的接口引用
private AOPMethod method;
/**
* 被代理的对象
*/
private Object o;
/**
*
* 创建一个新的实例 AOPHandle.
*
* @param o
* 委托类实例引用
* @param method
*/
public AOPHandle(Object o, AOPMethod method) {
this.o = o;
this.method = method;
}
/**
* 这个方法会自动调用,Java动态代理机制 会传入下面是个参数
*
* @param Object
* proxy 代理对象的接口,不同于对象
* @param Method
* method 被调用方法业务接口
* @param Object[]
* args 方法参数 不能使用invoke时使用proxy作为反射参数时,因为代理对象的接口,不同于对象
* 这种代理机制是面向接口,而不是面向类的
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
if (this.method != null) {
// 修改的地方在这里哦
this.method.before(proxy, method, args);
// 反射调用方法
ret = method.invoke(o, args);
// 修改的地方在这里哦
this.method.after(proxy, method, args);
} else {//无AOP的路径
System.out.println("invocation handler before");
ret = method.invoke(o, args);
System.out.println("invocation hander after");
}
return ret;
}
}
代理类动态生成:这个是生成动态代理类的核心
AnimalFactory.java
package proxy;
import java.lang.reflect.Proxy;
import proxy.annon.AnnoInjection;
import proxy.imp.AOPMethod;
/**
* 根据 传进来的委托实例引用创建并返回代理类引用
*
* @ClassName: AnimalFactory
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午4:13:37
*
*/
public class AnimalFactory {
/***
* 获取对象方法
*
* @param obj
* @return
*/
private static Object getAnimalBase(Object obj, AOPMethod method) {
// 获取代理对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new AOPHandle(AnnoInjection.getBean(obj), method));
}
/***
* 获取对象方法
*
* @param obj
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAnimal(Object obj, AOPMethod aopMethod) {
return (T) getAnimalBase(obj, aopMethod);
}
/***
* 获取对象方法
*
* @param className
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAnimal(String className, AOPMethod method) {
Object obj = null;
try {
obj = getAnimalBase(Class.forName(className).newInstance(), method);
} catch (Exception e) {
e.printStackTrace();
}
return (T) obj;
}
/***
* 获取对象方法
*
* @param clz
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAnimal(Class<?> clz, AOPMethod method) {
Object obj = null;
try {
obj = getAnimalBase(clz.newInstance(), method);
} catch (Exception e) {
e.printStackTrace();
}
return (T) obj;
}
}
然后就可以进行测试了
先进行注解的测试,看我在代码中注解的值到底有没有赋值到类实例中;
注解测试
package proxy;
import proxy.annon.AnnoInjection;
public class TestInjection {
/***
* 创建一个实例然后,通过注入逻辑自动将注解的内容赋值给实例属性
*/
public static void main(String[] args) throws InterruptedException {
DogImp dogImp = (DogImp) AnnoInjection.getBean(new DogImp());//等下还会介绍其中的处理逻辑
Thread.sleep(100);
System.out.println(dogImp.getName());
dogImp.getProperty();
}
}
输出结果:可以看到输出结果我没有对实例属性进行任何的赋值操作,但是最后两句输出已经可以看出属性已经有值了。前两句是注入逻辑中输出的log,可以看到进行了一次属性注入,一次方法注入。
注入 name 属性 Lumia
注入 setProperty 方法注解 水陆两栖战士
Lumia
Lumia= 水陆两栖战士
代理类测试
上面我们是建立了一个简单的基于接口的动态代理技术框架,动态代理技术主要有委托接口,以及委托类,调用处理器,代理类动态生成这些技术组成;下面给出代理类的测试案例。
package proxy;
import java.lang.reflect.Method;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
public class AOPTest {
public static void main(String[] args) {
/**
* 返回的dog为代理实例
*/
AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, null);//通过这个函数调用返回DogImp类的代理类;
//注意下这里为什么第二个参数为null,这个就是AOP切入的位置
dog.say();// 实体的一个行为
String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中
System.out.println(name1);
dog.setName("二狗子");
String name2 = "我的名字是" + dog.getName();
System.out.println(name2);
dog.getProperty();
}
}
输出结果:看到注解注入成功,然后在注解处理器中走得是无AOP的路径。
注入 name 属性 Lumia
注入 setProperty 方法注解 水陆两栖战士
invocation handler before
小狗:汪汪汪汪.....
invocation hander after
invocation handler before
invocation hander after
我的名字是= Lumia
invocation handler before
invocation hander after
invocation handler before
invocation hander after
我的名字是二狗子
invocation handler before
二狗子= 水陆两栖战士
invocation hander after
自定义注解并用于给属性赋值
定义注解
Seven.java文件
package proxy.annon;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义的注解
*
* @ClassName: Seven
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午4:20:44
*
*/
@Retention(RetentionPolicy.RUNTIME) // 表示这个注解可以生存到运行期
@Target({ ElementType.FIELD, ElementType.METHOD }) // 指定注解的使用范围
public @interface Seven {
// 设定注解的方法,注解方法没有方法体,可以设置默认值
public String value() default "小黑";
public String Property() default "无属性";
}
注解注入
注解自动注入是在程序定义一个空实例后,根据注解进行自动注入。根据刚才自定义的注解可以这个注解只能用于字段,函数中,所有自动注入逻辑主要是遍历一个变量的所有字段和函数,然后查看是否具有指定的Seven注解,然后读取我们在源码中设置的值并通过反射机制属性对应的setXXX方法完成初始化。
package proxy.annon;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/*
* 注解注入,将一个业务类实例的注解自动赋值给类属性
* @ClassName: AnnoInjection
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午4:22:52
*
*/
public class AnnoInjection {
public static Object getBean(Object obj) {
try {
// 获得类属性
Field f[] = obj.getClass().getDeclaredFields();
// 遍历属性,查找所有的属性注解
for (Field ff : f) {
// 获得属性上的注解
Seven s = ff.getAnnotation(Seven.class);// 返回ff属性的Seven类型的注解
if (s != null) {
System.err.println("注入 " + ff.getName() + " 属性" + "\t\t" + s.value());
// 反射调用public set方法,如果访问级别为private,那么可以直接使用属性的set(obj,
// value);
obj.getClass()
.getMethod("set" + ff.getName().substring(0, 1).toUpperCase() + ff.getName().substring(1), // 组配函数名称出来
new Class<?>[] { String.class })
.invoke(obj, s.value());// 通过反射调用属性对应的setXXX函数将注解的值赋值给类属性
}
}
// 获得所有方法,查找方法注解
Method m[] = obj.getClass().getDeclaredMethods();
for (Method mm : m) {
// 获得方法注解
Seven s = mm.getAnnotation(Seven.class);
if (s != null) {
System.err.println("注入 " + mm.getName() + " 方法注解" + "\t" + s.Property());
mm.invoke(obj, s.Property());// 通过方法注入注解的值
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
定义一个AOP接口用于AOP切面编程
上面只是使用了动态代理技术和注解技术,下面通过定义一个AOP接口,将AOP接口的能力添加到代理类中
AOP接口
package proxy.imp;
import java.lang.reflect.Method;
/**
* 这是一个AOP的一个切面;在每个接口方法中可以做一些类似于日志处理的功能
*
* @ClassName: AOPMethod
* @Description: TODO(这里用一句话描述这个类的作用)
* @author PengRong
* @date 2017年3月5日 下午4:18:20
*
*/
public interface AOPMethod {
// 实例方法执行前执行的方法,比如执行方法前,记录类状态,写入log.监控xx变量,,,
void after(Object proxy, Method method, Object[] args);
// 实例方法执行后执行的方法
void before(Object proxy, Method method, Object[] args);
}
通过在生产代理类的代码中将实现AOP接口的实例传递进去。
package proxy;
import java.lang.reflect.Method;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
public class AOPTest {
public static void main(String[] args) {
/**
* new AOPMethod() { // 这里写方法执行前的AOP切入方法
*
* @Override public void before(Object proxy, Method method, Object[]
* args) { if (method.getName().equals("getProperty")) {
* System.err.println("成功拦截" + method.getName() + "方法,启动"); }
* }
*
* // 这里系方法执行后的AOP切入方法
* @Override public void after(Object proxy, Method method, Object[]
* args) { if (method.getName().equals("getProperty"))
* System.err.println("成功拦截" + method.getName() + "方法,结束");
*
* } }
*/
/**
* 返回的dog为代理实例
*/
AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() { // 这里写方法执行前的AOP切入方法
@Override
public void before(Object proxy, Method method, Object[] args) {
if (method.getName().equals("getProperty")) {
System.err.println("成功拦截" + method.getName() + "方法,启动");
}
}
// 这里系方法执行后的AOP切入方法
@Override
public void after(Object proxy, Method method, Object[] args) {
if (method.getName().equals("getProperty"))
System.err.println("成功拦截" + method.getName() + "方法,结束");
}
});// 返回一个代理类
dog.say();// 实体的一个行为
String name1 = "我的名字是= " + dog.getName();// 通过这个可以看到可以将注解注入到属性中
System.out.println(name1);
dog.setName("二狗子");
String name2 = "我的名字是" + dog.getName();
System.out.println(name2);
dog.getProperty();
}
}
执行结果:通过实现AOP接口实例传递进去,那么将可以在实际业务方法执行前进行很多其他操作,比如统计,监控,日志功能。
注入 name 属性 Lumia
注入 setProperty 方法注解 水陆两栖战士
成功拦截getProperty方法,启动
成功拦截getProperty方法,结束
小狗:汪汪汪汪.....
我的名字是= Lumia
我的名字是二狗子
二狗子= 水陆两栖战士
综上:动态代理类技术是实现AOP的技术基础。
源代码
使用Java原生代理并实现注解自动注入