java 动态代理
1、一、打开eclipse,创建一个普通java工程 dynamicAgent 具体步骤为:
File -> New -> java Project,写好工程名,finish结束

2、二、在src下创建一个Student接口,包名用默认的就行,提供三个方法来获取Student的基本信息
具体步骤为:
鼠标右击 src -> New ->interface,写好接口名Student,finish结束
具体代码为:
package dynamicAgent;
/**
* 封装了学生的一些基本信息,具体由StudentImpl提供实现
* @return 姓名、性别、年龄
*/
public interface Student {
//获取学生姓名
public String getStudentName(String stuName);
//获取学生性别
public String getStudentSex(String stuSex);
//获取学生年龄
public int getStudentAge(int stuAge);
}

3、三、写一个StudentImpl类来实现Student接口
具体步骤为:
鼠标包名 -> Class ,写好类名StudentImpl,finish结束
具体代码为:
package dynamicAgent;
public class StudentImpl implements Student {
@Override
public String getStudentName(String stuName) {
return stuName;
}
@Override
public String getStudentSex(String stuSex) {
return stuSex;
}
@Override
public int getStudentAge(int stuAge) {
return stuAge;
}
}

4、四、写一个Main类来运行看一下控制台信息
具体步骤为:
鼠标包名 -> Class ,写好类名Main,finish结束
具体代码为:
package dynamicAgent;
public class Main {
public static void main(String[] args) {
Student stu = new StudentImpl();
String name = stu.getStudentName("西施");
System.out.println("我的姓名:" + name);
String sex = stu.getStudentSex("女");
System.out.println("我的性别:" + sex);
int age = stu.getStudentAge(18);
System.out.println("我的年龄:" + age);
}
}


5、五、重要的来了,假如需要我们写日志跟踪每个方法的开始和结束,实现如下图的显示信息,若不用动态代理,代码将会变得很分散和混乱,且需要修改的时候会变得很麻烦

6、六,若不用动态代理,我们就要去修改StudentImpl,因为这个类是具体实现方法的,而我们需要日志跟踪每个方法的开始和结束,这样做也能实现上述的显示
具体代码为:
package dynamicAgent;
public class StudentImpl implements Student {
@Override
public String getStudentName(String stuName) {
System.out.println("这个方法getStudentName开始了,正在获取,请稍等...");
String name=stuName;
System.out.println("这个方法getStudentName结束了,获取完毕");
return name;
}
@Override
public String getStudentSex(String stuSex) {
System.out.println("这个方法getStudentSex开始了,正在获取,请稍等...");
String sex=stuSex;
System.out.println("这个方法getStudentSex结束了,获取完毕");
return sex;
}
@Override
public int getStudentAge(int stuAge) {
System.out.println("这个方法getStudentAge开始了,正在获取,请稍等...");
int age=stuAge;
System.out.println("这个方法getStudentAge结束了,获取完毕");
return age;
}
}

7、七、这样做代码不易维护,而且工作量也变大了,怎么说呢?若是有成千上万个方法,这样做了,原有的业务方法就会急剧膨胀;而且,若要修改每条日志的显示信息,想想都会觉得很不舒服对吧。所以在没有优秀框架去解决的情况下,我们就需要去弄动态代理来解决这个问题。把StudentImpl改回原样

8、八、写一个StudentProxy类来实现动态代理
具体步骤为:
鼠标包名 -> Class ,写好类名StudentProxy,finish结束
具体代码为:
package dynamicAgent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class StudentProxy {
//要代理的对象
private Student stu;
//构造器初始化要代理的对象
public StudentProxy(Student stu) {
this.stu=stu;
}
//返回代理对象
public Student getLoggingProxy(){
Student proxy=null;
ClassLoader loader=stu.getClass().getClassLoader();
Class[] interfaces=new Class[]{Student.class};
InvocationHandler handler=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName=method.getName();
System.out.println("这个方法:"+ methodName +"开始运行了,正在获取,请稍等...");
Object result=null;
try {
result=method.invoke(stu, args);
} catch (NullPointerException e) {
e.printStackTrace();
}
System.out.println("这个方法:"+ methodName +"运行结束了,获取完毕");
return result;
}
};
/**
* loader: 代理对象使用的类加载器。
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* handler: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy=(Student) Proxy.newProxyInstance(loader, interfaces, handler);
return proxy;
}
}

9、九、修改main方法,看运行结果是一样的
具体代码为:
package dynamicAgent;
public class Main {
public static void main(String[] args) {
Student stu = new StudentImpl();
stu=new StudentProxy(stu).getLoggingProxy();
String name=stu.getStudentName("西施");
System.out.println("姓名:"+name);
String sex=stu.getStudentSex("女");
System.out.println("性别:"+sex);
int age=stu.getStudentAge(18);
System.out.println("年龄:"+age);
}
}

10、十、这样做,代码纯净多了,还易于维护,想要修改日志,只需要去StudentProxy修改两条日志显示就OK了,感觉爽了很多,若是加入spring框架,用spring的AOP,我们连StudentProxy类都不用写了,AOP直接帮我们搞定了,这就更爽了