代理的概念与作用
生活中的代理
武汉人从武汉的代理商手中买联想电脑和直接跑到北京来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上都一样吧,都解决了核心问题,但是,一点区别都没有吗?
程序中的代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理等等你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时添加系统功能代码。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是代理类,这样很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
AOP面向方面编程(Aspect oriented program,简称AOP)
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示
安全 事务 日志
StudentService ---------|----------|------------|------------
CoursetService ---------|----------|------------|------------
MiscService ---------|----------|------------|------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
-------------------------------------------------切面
……… ………… ……….
-------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面编程AOP,AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码运行效果是一样的,如下所示:
-------------------------------------------------切面
method1 method2 method3
{ { {
……… ………… ……….
} } }
-------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理,全部采用静态代理方式,将是一件非常麻烦的事情!写成百千上万个代理类,是不是太累。
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
CGLIB库可以动态生成一个类的子类,一个类的子类可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1. 在调用目标方法之前
2. 在调用目标方法之后
3. 在调用目标方法前后
4. 在处理目标方法异常的catch块中
使用Proxy类生成动态代理类
----------获取动态代理类对象的两种方法----------
动态代理类是在运行时指定接口列表的类(所以是动态创建的),构造函数参数是一个InvocationHandler对象,没有无参的构造函数,因为没有任何意义。
1.使用Proxy类静态方法getProxyClass()得到指定接口列表的动态代理类字节码通过构造函数创建动态代理类实例
Class proxyClass = Foo.class.gProxyClass(Foo.class.getClassLoader(),Foo.class);
Constructor constructor = (Constructor)proxyClass.getConstructor(InvocationHandler.class);
Foo f = (Foo)construcor.newInstance(
new InvocationHandler()
{
public Object invoke(Object proxy,Method method,Object... args)
{
//........
}
});
2.直接使用Proxy的静态方法getProxyInstance()构造函数获得动态代理类的实例。因为静态代理类代理某个接口实现类所以它的类型也是接口类型。
Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),Foo.class,
new InvocationHandler()
{
public Object invoke(Object target,Method method,Object[] arg)
{
}
});
---Proxy动态代理类的原理内部实现,以代理实现Collection接口的类为例------
动态代理类在调用方法时,方法内部调用InvocationHandler类型的实例对象handler的invoke方法。handler对象的invoke方法通过调用目标对象的相应方法并返回结果。
动态代理类内部实现
成员变量
private InvocationHandler handler;
构造方法
protected Proxy(InvocationHandler handler)
{
this.handler=handler;
}
成员方法
size()
{
return handler.invoke(this,this.getClass().getMethod("size"),Object... args);
}
InvocationHandler接口实现类
Object target;
InvocationHandler handler=new InvocationHandler()
{
public Object invoke(Object proxy,Method method,Object... args)
{
return method.invoke(target,args);
}
};
通告Advice和MyAdvice
将要添加的系统功能定义到Advice接口中
interface Advice
{
void beforMethod(Method method);
void afterMethod(Method method);
}
MyAdvice实现Advice并实现具体系统功能
class MyAdvice implements Advice
{
long startTime = 0;
public void beforMethod(Method method)
{
startTime=System.currentTimeMillis();
System.out.println("the method is :"+method.getName());
System.out.println("--------欢迎来到黑马训练营!--------");
}
public void afterMethod(Method method)
{
long endTime=System.currentTimeMillis();
System.out.println("the method is :"+method.getName());
System.out.println("--------恭喜你从黑马毕业啦!--------");
System.out.println("--------培训时间:"+(endTime-startTime)+"ms--------");
}
}
类似Spring的AOP框架
FactoryBean类
/**
FactoryBean负责创建目标类或代理类的实例对象,并通过配置文件实现切换。
其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中不是ProxyFactoryBean
则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
*/
class FactoryBean
{
private Properties p = new Properties();
//构造函数初始化加载配置文件
FactoryBean(InputStream ips)
{
try
{
p.load(ips);
}
catch (IOException e)
{
throw new RuntimeException("加载配置文件失败");
}
}
//获取bean对象
public Object getBean(String key) throws Exception //为了简化代码便于阅读,没有处理异常
{
String className = p.getProperty(key);
Object bean = Class.forName(className).newInstance();
if(bean instanceof ProxyFactoryBean)
{
Object proxy = null;
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
Advice advice = (Advice)Class.forName(p.getProperty(key+".advice")).newInstance();
Object target = Class.forName(p.getProperty(key+".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
return proxy;
}
return bean;
}
}
动态代理工厂类ProxyFactoryBean
/**
充当封装生成动态代理的工厂,需要为工厂提供配置参数信息,目标和通告。
*/
class ProxyFactoryBean
{
private Advice advice;
private Object target;
/*
获取代理对象
*/
public Object getProxy()
{
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler()
{
public Object invoke(Object proxy,Method method,Object[] args) throws Exception
{
advice.beforMethod(method);
Object value = method.invoke(target,args);
advice.afterMethod(method);
return value;
}
});
return proxy;
}
/*
创建advice和target的set及get方法
*/
public void setAdvice(Advice advice)
{
this.advice = advice;
}
public void setTarget(Object target)
{
this.target = target;
}
public Advice getAdvice()
{
return advice;
}
public Object getTarget()
{
return target;
}
}
通告类Advice
/**
通告Advice接口,定义系统功能抽象方法
*/
interface Advice
{
void beforMethod(Method method);
void afterMethod(Method method);
}
实现Advice接口的类MyAdvice类,实现具体的系统功能
/**
实现Advice接口的类MyAdvice类
*/
class MyAdvice implements Advice
{
long startTime = 0;
public void beforMethod(Method method)
{
startTime=System.currentTimeMillis();
System.out.println("the method is :"+method.getName());
System.out.println("--------欢迎来到黑马训练营!--------");
}
public void afterMethod(Method method)
{
long endTime=System.currentTimeMillis();
System.out.println("the method is :"+method.getName());
System.out.println("--------恭喜你从黑马毕业啦!--------");
System.out.println("--------培训时间:"+(endTime-startTime)+"ms--------");
}
}