Java实现简单的动态代理
先写一个简单的例子来说明为什么需要代理。
1.编写接口UserManager.java
package proxy;
public interface UserManager {
public void addUser(int id, String name, int age);
public void deleteUser(int id);
public String getUserName(int id);
}
2.编写实现类UserManagerImpl.java
package proxy;
public class UserManagerImpl implements UserManager {
@Override
public void addUser(int id, String name, int age) {
System.out.println("------------addUser()-----------");
}
@Override
public void deleteUser(int id) {
System.out.println("------------deleteUser()-----------");
}
@Override
public String getUserName(int id) {
System.out.println("------------getUserName()-----------");
return null;
}
}
3.编写客户端类Client.java对UserManager实现进行测试。package proxy;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
UserManager manager = new UserManagerImpl();
manager.addUser(1, "zhangsan", 24);
}
}
输出结果为------------addUser()----------- 现在,想在所有UserManager函数调用之前,先对用户信息进行安全性检查,就需要在所有的函数执行前添加安全性检查,如代码UserManagerImpl.java所示。
package proxy;
public class UserManagerImpl implements UserManager {
@Override
public void addUser(int id, String name, int age) {
security();
System.out.println("------------addUser()-----------");
}
@Override
public void deleteUser(int id) {
security();
System.out.println("------------deleteUser()-----------");
}
@Override
public String getUserName(int id) {
security();
System.out.println("------------getUserName()-----------");
return null;
}
private void security(){
System.out.println("------------security()--------------");
}
}
这样实现会打破程序设计的封装性,如果需要添加功能就需要需改程序的源代码,因此这里就需要使用代理的思想。 增加一个代理,代理类可以控制原对象,所以在调用原对象之前就可以进行安全性检查。
添加代理类UserManagerImplProxy.java
package proxy;
public class UserManagerImplProxy implements UserManager {
private UserManager manager;
public UserManagerImplProxy(UserManager manager){
this.manager = manager;
}
@Override
public void addUser(int id, String name, int age) {
security();
this.manager.addUser(id, name, age);
}
@Override
public void deleteUser(int id) {
security();
this.manager.deleteUser(id);
}
@Override
public String getUserName(int id) {
security();
return this.manager.getUserName(id);
}
private void security(){
System.out.println("------------security()--------------");
}
}
将实际对象通过构造函数传递给代理类,在调用实际对象方法前添加对安全性的检查security()方法,通过使用代理将需要添加的功能从UserMangerImpl类中分离出来
修改后的Client.java代码如下。
package proxy;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
// UserManager manager = new UserManagerImpl();
// manager.addUser(1, "zhangsan", 24);
UserManager manager = new UserManagerImplProxy(new UserManagerImpl());
manager.addUser(1, "zhangsan", 24);
}
}
程序输出为------------security()--------------------------addUser()-----------
代理模式:在实际开发当中使用频率非常高的一种设计模式。
定义:为其他对象提供一种代理机制以控制这个对象的访问。
代理对象要与目标对象的接口一致。
以上是简单代理的实现方式,在实际使用中,使用最多的是动态代理。代理对象会在运行期生成。
编写安全性检查的处理方法类SecurityHandler.java
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SecurityHandler implements InvocationHandler {
private Object targerObject;
public Object newProxyObject(Object targetObject){
this.targerObject = targetObject;
Object object = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
<span style="white-space:pre"> </span>targetObject.getClass().getInterfaces(),
this);
return object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
security();
Object result = null;
try{
result = method.invoke(this.targerObject, args);
}catch(Exception e){
e.printStackTrace();
}
return result;
}
private void security(){
System.out.println("------------security()--------------");
}
}
下面对动态代理的实现进行说明 1.需要实现InvocationHandler接口,并实现这个接口当中的invoke方法。当调用代理对象的方法的时会自动调用invoke方法,所以可以将安全性检查的方法放到invoke方法中,这样在调用目标对象的方法前就会调用security()方法了。
2.在SecurityHandler方法中声明了一个Object类型的实例变量,用来存储目标对象。
3.编写newProxyObject方法,这个方法将目标对象赋值给实例变量targerObject来保存目标对象。
4.调用Proxy的newProxyInstance函数来创建一个代理对象,该函数有3个参数,分别是ClassLoader,接口数组(Class<?>[])以及一个InvocationHandler。
JVM在加载类的时候就是通过调用ClassLoader的loadClass()方法来加载类的,而第二个参数是一个接口数组,这就是说明被代理的类需要实现某个接口,当然也可以不实现接口,使用CGLIB技术,这个技术不在本文讨论范围内。第三个参数是一个InvocationHandler,这里置为this即可。
修改Client.java
package proxy;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
// UserManager manager = new UserManagerImpl();
// manager.addUser(1, "zhangsan", 24);
// UserManager manager = new UserManagerImplProxy(new UserManagerImpl());
// manager.addUser(1, "zhangsan", 24);
SecurityHandler proxy = new SecurityHandler();
<span style="white-space:pre"> </span>UserManager manager = (UserManager)proxy.newProxyObject(new UserManagerImpl());
<span style="white-space:pre"> </span>manager.addUser(1, "zhangsan", 24);
}
}
程序输出为------------security()--------------
------------addUser()-----------
创建一个SecurityHandler对象,然后创建一个目标对象,调用newProxyObject函数获得这个对象的一个代理对象,再调用代理对象的方法,这样即可实现动态代理。