---------------------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培訓、期待與您交流!
--------------------