---------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------
1. 代理
1.概述
代理:就是当一个类中的源代码不能更改,但是需要我们需要一个代理,代理类和原来的 类都是实现的相同的接口,所以代理类中的方法和原类具有相同的方法,那么我们就使用此接口来操作代理类,而不是直接操作原类,可以进行完善方法…等。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。
交叉业务的编程问题即为面向方面的编程(Aspect oriented program),其目标就是将交叉业务模块化,例如:安全,事务,日志等功能。
JVM生成的动态类必须实现一个或者多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,如果一个要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类必须和要代理的类实现相同的接口或者类。
代理的各个方法除了通常要调用目标的相应方法和对外返回目标返回结果外,还可以在代理方法中的一下位置加上系统功能代码:
调用目标方法之前
调用目标方法之后
调用目标方法前后
在处理目标方法异常的catch代码中
注意:StringBuilder和StringBuffer的区别:用法是一样的,要是单线程的话,用StringBuilder,对线程用StringBuffer,安全。
2.代理类构造函数和方法
Proxy类,我们获得Connection接口的代理类后,然后查看代理类中的构造函数和方法。输出格式:函数名(参数类型)
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyDemo {
public static void main(String[] args) {
/*获得Collection的代理类
* Proxy.getProxyClass(指定加载器,实现的接口);*/
Class clazzPro = Proxy.getProxyClass(Collection.class.getClassLoader(),
Collection.class);
System.out.println("Collcetion的代理类:"+clazzPro.getName());
/**
* 获得代理的构造函数 构造函数名(参数类型)
* 思路:获得构造函数的数组,然后利用循环遍历数组
* 把获得的每个构造函数的参数类型数组。
* 然后在遍历参数数组,组成字符串
* for(遍历构造函数的数组){
* StringBuilder拼接
* for(遍历参数类型数组){
* StringBuilder拼接
* }}
*
*/
System.out.println("---------------Conncetion的代理类中的构造函数---------------");
StringBuilder sb=null;
Constructor [] constructors=clazzPro.getConstructors();
for(Constructorconstructor:constructors){
sb=new StringBuilder();
sb.append(constructor.getName());
sb.append("(");
Class[]clazzParam=constructor.getParameterTypes();
for(Classparam:clazzParam){
sb.append(param.getName()).append(",");
}
if(clazzParam!=null){
sb.deleteCharAt(sb.length()-1);
}
sb.append(")");
System.out.println(sb.toString());
}
/**
* 获得代理的方法 方法名(参数类型)
* 思路:获得方法的数组,然后利用循环遍历数组
* 把获得的每个方法的参数类型数组。
* 然后在遍历参数数组,组成字符串
* for(遍历方法的数组){
* StringBuilder拼接
* for(遍历参数类型数组){
* StringBuilder拼接
* }}
*
*/
System.out.println("---------------Method的代理类中的方法---------------");
Method [] methods=clazzPro.getMethods();
for(Methodmethod:methods){
sb=new StringBuilder();
sb.append(method.getName());
sb.append("(");
Class[]clazzParam=method.getParameterTypes();
for(Classparam:clazzParam){
sb.append(param.getName()).append(",");
}
if(clazzParam!=null &&clazzParam.length!=0){
sb.deleteCharAt(sb.length()-1);
}
sb.append(")");
System.out.println(sb.toString());
}
}
}
部分结果:
Collcetion的代理类:com.sun.proxy.$Proxy0
---------------Conncetion的代理类中的构造函数---------------
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
---------------Method的代理类中的方法---------------
hashCode()
equals(java.lang.Object)
toString()
add(java.lang.Object)
contains(java.lang.Object)
isEmpty()
size()
toArray()
toArray([Ljava.lang.Object;)
addAll(java.util.Collection)
3.代理类的实例对象
import java.lang.reflect.Constructor;
importjava.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyDemo1 {
public static void main(String[] args)throws Exception{
Class clazzProxy=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);//获得代理类
/**
* 第一种:获得代理类的对象
* 思路:1.从上面知道,Collection的构造方法的参数是InvocationHandler类型
* 2.使用使用反射获得构造函数,然后实例化对象,new Instance
* 3.InvocationHandler是一个接口,传参数的时候,那么我们就定义一个子类实现此接口
*
*/
/*这是;为了传参数提前定义的类*/
class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {
return null;
}
}
Constructor constructor=clazzProxy.getConstructor(InvocationHandler.class);//获得了构造函数
Collection Proxy1=(Collection)constructor.newInstance(newMyInvocationHandler() );//获得实例对象
/**
* 第二种方法使用内部类:但是也是先获得构造函数,然后使用内部类对象作为参数获得对象
*/
Collection Proxy2=(Collection)constructor.newInstance(newInvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable {
return null;
}
});
/**
* 方法3:不需要先获得构造函数。直接使用代理类(Proxy)的静态方法(newProxyInstance),就直接获得代理类的实例对象
* newProxyInstance(类加载器,Class[]数组{实现的接口字节码},InvocationHandlerle参数)
*/
Collection Proxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
newClass[]{Collection.class},
newInvocationHandler(){
ArrayList target=new ArrayList();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
Object values=method.invoke(target, args);
return values;
}
}
);
/*对于内部类中的Invoke方法,上面的都可以写成实例对象Proxy3过程的一样*/
Proxy3.add("abc");
Proxy3.add("123");
Proxy3.add("zhansgan");
System.out.println(Proxy3.size());//3
}
}
每调用一次Collecton的方法,就是Proxy3每调用一次方法,那么就会执行一次Invoke方法。
4.代理的调用原理
当代理类中需要了hashCode
、equals
或toString
方法,那么会交给
InvocationHandler来实现,其他的不会交给
InvocationHandler 实现。invoke(Objectproxy, Method method,Object[] args)方法中的参数:被代理的类,代理类的方法,方法的参数。
System.out.println(Proxy3.getClass().getName());结果:com.sun.proxy.$Proxy0 却不是ArrayList
5.代理实例(框架)
把目标和方法前后的都封装成对象,就可以代理任何对象和对其修饰
/**
* 思路:
* 1.将获得代理类对象的方法封装成一个方法,主要参数是:目标对象,系统代理
* 2.要把外部的目标对象用final修饰,因为是内部类访问外部的变量需要把变量定义为final
* 3.外部的建议接口,然后实现自己建议类。
* 4.最后那调用,把定义的目标和建议对象传给获得代理对象的方法
*/
/**
* 建议接口
*/
public interface Advice {
/*方法前*/
void afterMethod(Method method);
/*方法后*/
void beforeMethod(Method method);
}
import java.lang.reflect.Method;
/**
* 实现建议接口
*/
public class MyAdvice implements Advice {
long startTime = 0;
@Override
public void afterMethod(Method method) {
System.out.println("结束");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "run..." + (endTime - startTime));
}
@Override
public void beforeMethod(Method method) {
System.out.println("开始");
this.startTime = System.currentTimeMillis();
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
/**
* 思路:
* 1.将获得代理类对象的方法封装成一个方法,主要参数是:目标对象,系统代理
* 2.要把外部的目标对象用final修饰,因为是内部类访问外部的变量需要把变量定义为final
* 3.外部的建议接口,然后实现自己建议类。
* 4.最后那调用,把定义的目标和建议对象传给获得代理对象的方法
*/
public static void main(String[]args) {
final ArrayList target = new ArrayList();
Collection Proxy3 = (Collection) getProxy(target,new MyAdvice());
/* 对于内部类中的Invoke方法,上面的都可以写成实例对象Proxy3过程的一样 */
Proxy3.add("abc");
Proxy3.add("123");
Proxy3.add("zhansgan");
System.out.println(Proxy3.size());// 3
System.out.println(Proxy3.getClass().getName());
}
private static Object getProxy(final Object target, final Advice advice) {
Object Proxy3 = Proxy.newProxyInstance(target.getClass()
.getClassLoader(), target.getClass().getInterfaces(),/*获得实现的接口数组*/
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.beforeMethod(method);// 前
Object values = method.invoke(target, args);
advice.afterMethod(method);// 后
return values;
}
});
return Proxy3;
}
}
结果:
开始
结束
addrun...0
开始
结束
addrun...0
开始
结束
addrun...0
开始
结束
sizerun...0
3
com.sun.proxy.$Proxy0
以后我们要是想改变目标的代理。只要自定义对象后,传到方法中即可,对于我们在方法前后的代码,可以用自定义的MyAdvice类的对象来实现。这就是相当于一个框架
2. 代理的应用
有配置文件,当配置文件中的配置信息更改了,那么代理类获得的对象也会改变。那么目标类和建议类也会随着改变的。
自定义的建议类
package www.fuxi.jiaqiang1;
import java.lang.reflect.Method;
/**
* 建议接口
*/
public interface Advice {
/*方法前*/
void afterMethod(Method method);
/*方法后*/
void beforeMethod(Method method);
}
package www.fuxi.jiaqiang1;
import java.lang.reflect.Method;
/**
* 实现建议接口
*/
public class MyAdvice implements Advice {
long startTime = 0;
@Override
public void afterMethod(Method method) {
System.out.println("结束");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "run..." + (endTime - startTime));
}
@Override
public void beforeMethod(Method method) {
System.out.println("开始");
this.startTime = System.currentTimeMillis();
}
}
下面模拟的是工厂模式
package www.fuxi.jiaqiang1.aopframe;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import www.fuxi.jiaqiang1.Advice;
/*代理工厂
* 定义了建议类对象和目标对象
* 也可以改变的*/
public class ProxyFactoryBean {
private Advice advice;
private Object target;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getTarger() {
return target;
}
public void setTarger(Object targer) {
this.target = targer;
}
/*获得代理类对象*/
public Object getProxy() {
Object proxy3 = Proxy.newProxyInstance(target.getClass()
.getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.beforeMethod(method);
Object reva = method.invoke(target, args);
advice.afterMethod(method);
return reva;
}
});
return proxy3;
}
}
下面获得配置文件的信息,并且通过工厂框架来获得相应的代理类后,然后调用相应的建议类和目标类
package www.fuxi.jiaqiang1.aopframe;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import www.fuxi.jiaqiang1.Advice;
/**
* 获得配置文件的信息
*/
public class BeanFactory {
Properties props = new Properties();//配置文件对象
/*初始化*/
public BeanFactory(InputStream ips) {
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获得代理工厂的对象,这里面调用的代理工程的对象
* @param name
* @return
*/
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();
}
/* 假如是自定义的代理,那么就执行ProxyFactoryBean中程序
* 就要给Advice和target传值,
* Advice那么就要xxx.advice=www.fuxi.jiaqiang1.MyAdvice
* target就是xxx.target=java.util.ArrayList
* 当然,如果配置文件中的变化了,那么Advice和target也随着改变*/
if (bean instanceof ProxyFactoryBean) {
Object proxy = null;
try {
ProxyFactoryBean proxyBean = (ProxyFactoryBean) bean;
Advice advice = (Advice) Class.forName(
props.getProperty(name + ".advice")).newInstance();
Object targer = Class.forName(
props.getProperty(name + ".target")).newInstance();
proxyBean.setAdvice(advice);
proxyBean.setTarger(targer);
proxy = proxyBean.getProxy();
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
下面是测试类
package www.fuxi.jiaqiang1.aopframe;
import java.io.InputStream;
/**
* 测试类
* @author yang
*
*/
public class ApoFrameworkText {
public static void main(String[] args) {
InputStream in=ApoFrameworkText.class.getResourceAsStream("config.properties");
Object bean=new BeanFactory(in).getBean("xxx");
System.out.println(bean.getClass().getName());
}
}
结果:
com.sun.proxy.$Proxy0
---------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------