------- android培训、java培训、期待与您交流! ----------
代理的概念和作用:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务处理等等.你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用的目标类的方法周围加上系统功能代码。
在客户端用接口来引用:如果想用代理,就传递代理的对象,如果想用目标,就传递目标的对象。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端的程序,在配置文件中配置是使用目标类还是使用代理类。这样以后很容易切换。
譬如:想要日志功能就配置代理类,否则配置目标类。这样增加系统功能很容易,以后去掉系统功能也很容易。
Interface InterfaceA{
void dosomething();
}
X implements InterfaceA{
void dosomething(){
}
}
XProxy implements InterfaceA{
void dosomething(){
beforetime…
new X().dosomething();
endtime;
}
}
class Client{
public static void main(String[] args){
InterfaceA obj=Class.forName(“xxxxxxxxxxx”).newinstance;
}
}
这样客户端的代码不用修改,只用修改配置文件就可以了。配置文件中配置到底调用的是类名。
什么是交叉业务:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
用Collection接口生成的代理类,目标可以是ArrayList,也可以是Hashset。因为它们有相同的接口。
生成的动态类要和目标类具有相同的方法(此处说的不太严谨,因为目标类中的某些方法在接口中并没有定义,而是目标类
如果一个类没有实现任何接口,怎样为该类动态生成一个代理呢?
CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
总之,代理类要有和目标类中所有方法签名相同的方法。
不过用接口方式生成动态类的话,生成的动态类中的方法仅仅是接口中的所有方法,另外因为生成的是类,所以会有Object
而用CGLIb库的话,因为是继承,生成的动态类会有父类所有的方法。
java.lang.reflect
类 Proxy 中的方法
static Class<?> | getProxyClass(ClassLoader |
这个方法就是在内存中造出一份字节码。在造出字节码过程中,要指定该类实现了什么接口。因为该字节码是直接在内存中生成的,所以没有用类加载器加载。但是为了统一,所有的字节码都关联的类加载器。所以此处指定一个类加载器即可,可以随意指定,但是一般情况下指定为所实现的某个接口关联的类加载器。
让JVM创建动态类及其实例对象,需要给他提供哪些信息?
1. 生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知。
2. 产生的类字节码必须有个关联的类加载器对象。
3. 生成的类中的方法的代码是怎样的,也是由我们提供。把我们的代码写在一个约定好的接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码,提供执行代码的那个对象就是那个InvocationHandler对象。它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用了。
创建动态代理的几种方式:
public class ProxyTest {
@Test
public void test1() throws Exception {
// 先在内存中创建一个实现某接口的动态类字节码
Class clazzProxy = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
// 获取该字节码的构造方法的对象。
Constructor constructor = clazzProxy
.getConstructor(InvocationHandler.class);
// 实现InvocationHandler接口的子类。
class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
// 调用构造方法对象,传入InvocationHandler的子类对象,创建出动态类的对象。
Collection proxy = (Collection) constructor
.newInstance(new MyInvocationHandler());
// Collection的clear方法返回值为void,运行不会出错。
proxy.clear();
// Collection的size方法有返回值,运行出错了,这是因为返回的null,无法转变成为int类型的数据。
// System.out.println(proxy.size());
}
@Test
public void test5() throws Exception {
Class clazzProxy = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
Constructor constructor = clazzProxy
.getConstructor(InvocationHandler.class);
// 此处与test4的不同之处就在于用匿名内部类的形式实现了InvocationHandler接口。
Collection proxy = (Collection) constructor
.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return null;
}
});
System.out.println(proxy);
}
@Test
// 将分步合为一步来完成。
public void test6() throws Exception {
Collection proxy = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
return null;
}
});
System.out.println(proxy);
}
@Test
// 加入了调用目标类对象的方法的代码。
public void test7() throws Exception {
Collection proxy = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class }, new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
/*
* 这个method是proxy.getClass.getMethod("methodname")得来的,而不是
* target.getClass.getMethod("methodname");我想应该都是一样的吧。可
* 作用于目标对象。
*/
Object retVal = method.invoke(target, args);
// 返回方法执行后的结果。
return retVal;
}
});
proxy.add("zxx");
proxy.add("lhm");
// 此时调用了有返回值的方法不会出错。
System.out.println(proxy.size());
}
@Test
// 加入了功能代码
public void test8() throws Exception {
Collection proxy = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class }, new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long begintime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endtime = System.currentTimeMillis();
System.out.println(method.getName() + " run time is: "
+ (endtime - begintime));
return retVal;
}
});
proxy.add("zxx");
proxy.add("lhm");
System.out.println(proxy.size());
}
}
动态类内部的代码猜想:
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
$Proxy0 implements Collection
{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
//生成的Collection接口中的方法的运行原理
int size()
{
return handler.invoke(this,this.getClass().getMethod("size"),null);
}
void clear(){
handler.invoke(this,this.getClass().getMethod("clear"),null);
}
boolean add(Object obj){
handler.invoke(this,this.getClass().getMethod("add"),obj);
}
}
InvocationHandler 接口中定义的invoke方法接收的三个参数是什么意思呢?
Client程序调用
proxy.add(“abc”)(注意这里是代理对象在调用代理的add方法)方法时候,涉及到三个要素:
proxy对象,代理的add方法,add方法的参数值。
Class $ Proxy0{
add(Object object) {
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
因为在调用代理的add方法的时候,add方法中就交给handler对象的invoke方法进行处理。
第一个参数的意思是:处理哪个代理类对象。
第二个参数的意思是:处理代理类对象的哪个方法。
第三个参数的意思是:该方法需要接受的参数值。
为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。所以打印的结果是:$Proxy数字 这个名称。
为了扩展性,也可以说将此做成框架的时候.会出现两点问题:
系统功能如果硬编码在InvocationHanlder的invoke方法中,灵活性太差。而应该是用户需要什么功能,自己编写。
如果将一段代码传递到invoke方法内去执行。
此处和线程的处理方式很类似:可以将功能代码封装在一个类的方法中,将该类的对象传递给InvocationHandler,在invoke方法中,调用该类对象的方法也就是执行了功能代码。
另外,目标对象也不能硬编码在InvocationHandler对象中,而是应该传递进去。如果硬编码的话,该代理框架只能用于某一个目标。
如下所示的就是一个不好的示例:
功能代码硬编码在invoke方法中,目标对象硬编码在InvocationHandler对象中。
publicvoid test8() {
Collection proxy = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class },new InvocationHandler() {
ArrayList target =newArrayList();
@Override
public Object invoke(Object proxy, Method method,
Object[] args)throws Throwable {
long begintime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endtime = System.currentTimeMillis();
System.out.println(method.getName()+ " run time is: "
+(endtime - begintime));
return retVal;
}
});
proxy.add("zxx");
proxy.add("lhm");
System.out.println(proxy.size());
}
之前的示例都是直接用接口来生成代理对象,而真实的用法是用传入的目标对象来获与配置取该目标类所实现的接口。
用于为某个对象生成和返回其代理对象,源对象必须实现接口,生成的代理对象会实现与源对象相同的接口。
以下是比较好的处理方式:
1.
/*
* 要说明的是,Advice接口中的方法可以接收InvocationHandler
* 的invoke方法的参数来用。
* 就比如aftermethod就定义了method参数,该方法可以在内部用
* 从invoke传递来的method。
*/
public interface Advice {
void beforeMethod();
void afterMethod(Method method);
}
2.
public class MyAdvice implements Advice {
long beforetime = 0;
@Override
public void beforeMethod() {
beforetime = System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
long endtime = System.currentTimeMillis();
System.out.println(method.getName() + " run time is :"
+ (endtime - beforetime));
}
}
3.
public class ProxyTest {
public static void main(String[] args) {
// 这样就将getProxy做成了一个小框架,以后用户只需要做的就是配置目标对象
// 以及编写系统功能代码就可以了。生成代理操作就不用再由用户来写了。
final ArrayList target = new ArrayList();
final Advice advice = new MyAdvice();
Collection proxy = (Collection) getProxy(target, advice);
proxy.add("hhh");
proxy.add("lim");
proxy.add("zxx");
System.out.println(proxy.size());
}
private static Object getProxy(final Object target, final Advice advice) {
Object proxy = Proxy.newProxyInstance(target.getClass()
. getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.beforeMethod();
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
});
return proxy;
}
}
实现AOP功能的封装与配置
具体步骤如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
Properties props = new Properties();
public BeanFactory(InputStream ips){
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getBean(String name){
String className = props.getProperty(name);
Object bean = null;
try {
Class clazz = Class.forName(className);
bean = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean){
Object proxy = null;
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
try {
Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
private Advice advice;
private Object target;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy() {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
/*new Class[]{Collection.class},*/
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/*long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
return retVal;*/
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
((Collection)bean).clear();
}
}
-------android培训、java培训、期待与您交流! ----------
详细请查看:http://edu.csdn.net/heima