------------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
纠结了2周的代理, 终于在今晚告一段落了, 废话不多说,开始总结:
所谓的代理,其实就是通过一个对象去调用其封装好的某一个方法,它在其他类的各个方法中增加了一些系统功能,例如,异常处理、日志、计算方法的运行时间、事物管理、等等。
通常情况下,我们都是采用动态代理,我们可以自定义一个目标类,借由代理类来实现它。
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
代理分为两种,静态代理以及动态代理
静态代理:
系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,也就是说,我们需要一个个的将类写死,比如说要实现什么样的接口,接口里所带的参数等等, 要写死,这样很不利于开发。
动态代理:
于是就产生出了动态代理的概念,由我们自己去配置一个类,交由Java虚拟机去识别。(请看概念)
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
获取代理类
//需要一个类加载器,和一个类实现的接口
Class
clazzProxy1
=
Proxy.
getProxyClass(
Collection.
class.
getClassLoader(),
Collection.
class); //打印结果是 $Proxy0
获取代理类的构造方法
Constructor[]
constructors
=
clazzProxy1.
getConstructors();
//返回代理类里的所有构造方法
for(
Constructor
constructor :
constructors){
String
name
=
constructor.
getName();
//返回构造方法的方法名
StringBuilder
sBuilder
=
new
StringBuilder(
name);
//由于不知道是不是有参的, 所以手动拼写代码
sBuilder.
append(
'(');
Class[]
clazzParams
=
constructor.
getParameterTypes();
//返回构造方法的参数类型
for(
Class
clazzParam :
clazzParams){
sBuilder.
append(
clazzParam.
getName()).
append(
',');
}
if(
clazzParams
!=
null
&&
clazzParams.
length
!=
0)
// 个人认为可以将这部放在前面先判断
sBuilder.
deleteCharAt(
sBuilder.
length()
-1);
sBuilder.
append(
')');
System.
out.
println(
sBuilder.
toString());
//拼接成一个构造方法 xxx(xx);
//这里打印的结果是$Proxy0(java.lang.reflect.InvocationHandler)
//InvocationHandler 是每一个代理处理程序所实现的一个接口,
//也就是说代理要处理程序,就必须实现InvocationHandler接口
}
返回代理类里的各个方法
for(
Method
method :
methods){
String
name
=
method.
getName();
StringBuilder
sBuilder
=
new
StringBuilder(
name);
sBuilder.
append(
'(');
Class[]
clazzParams
=
method.
getParameterTypes();
for(
Class
clazzParam :
clazzParams){
sBuilder.
append(
clazzParam.
getName()).
append(
',');
}
if(
clazzParams
!=
null
&&
clazzParams.
length
!=
0)
sBuilder.
deleteCharAt(
sBuilder.
length()
-1);
sBuilder.
append(
')');
System.
out.
println(
sBuilder.
toString());
} //代码同构造方法 不做解释
创建代理的实例化对象 以Collection 为例
class
MyInvocationHander1
implements
InvocationHandler{
public
Object
invoke(
Object
proxy,
Method
method,
Object[]
args)
throws
Throwable {
// TODO Auto-generated method stub
return
null;
}
}
Collection
proxy1
= (
Collection)
constructor.
newInstance(
new
MyInvocationHander1());
//用代理获取实例对象必须传入InvocationHandlder接口,
//因为InvocationHandlder是调用处理程序实现的接口。
System.
out.
println(
proxy1);
proxy1.
clear();
// proxy1.size(); //有返回值的方法不能被成功调用,因为invoke方法返回的是null值
Collection
proxy2
= (
Collection)
constructor.
newInstance(
new
InvocationHandler(){
//匿名内部类写法
public
Object
invoke(
Object
proxy,
Method
method,
Object[]
args)
throws
Throwable {
return
null;
}
}); //同上 不解释
/*
代理类实现的三个步骤, 第一,必须是有代理类的目标实例对象。 第二,代理类的类加载器。 第三,代理类的实现接口
*/
Collection
proxy3
= (
Collection)
Proxy.
newProxyInstance(
Collection.
class.
getClassLoader(), //目标类的类加载器
new
Class[]{
Collection.
class}, //目标类
new
InvocationHandler(){
ArrayList
target
=
new
ArrayList();
public
Object
invoke(
Object
proxy,
Method
method,
Object[]
args)
throws
Throwable {
System.
out.
println(
"我是ArrayList的HashCode: "
+
target.
hashCode());
Object
retVal
=
method.
invoke(
target,
args);
System.
out.
println(
retVal.
getClass().
getName());
return
retVal;
}
}
);
proxy3.
add(
"zxx");
//每add一次就调用一次invoke方法
proxy3.
add(
"lhm");
proxy3.
add(
"bxd");
System.
out.
println(
proxy3.
size());
//System.out.println(proxy3.getClass().getName());());
//从object继承的方法只有hashCode equals toString派发给InvocationHandler去做。
为代理添加一些自定义的功能,那么,这个接口我们可以自定义,比如:计算方法的时间
首先需要一个接口。
public
class
ProxyFactoryBean {
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() {
// TODO Auto-generated method stub
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);
//自定义接口的方法,
//这里的invoke,延续了代理类的方法,如果代理类调用了set方法, 那么
//在这里会继续将set方法引入给目标类使用
Object
retVal
=
method.
invoke(
target,
args);
advice.
afterMethod(
method);
//自定义接口的方法
return
retVal;
}
}
);
return
proxy3;
}
}
AOP的定义
Spring框架里有一个AOP的概念, 所以的AOP,是面向切面的编程, 也就是说,在框架的基础上添加事物等一系列的功能。
根据配置文件获取到动态代理的例子:
public
class
ProxyFactoryBean {
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(),
target.
getClass().
getInterfaces(),
new
InvocationHandler(){
public
Object
invoke(
Object
proxy,
Method
method,
Object[]
args)
throws
Throwable {
advice.
beforeMethod(
method);
Object
retVal
=
method.
invoke(
target,
args);
advice.
afterMethod(
method);
return
retVal;
}
}
);
return
proxy3;
}
public
class
BeanFactory {
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
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();
//在代理类里将接口与目标类作为变量传入, 所以这里只需要set就可以执行代理类里的方法
proxyFactoryBean.
setAdvice(
advice);
proxyFactoryBean.
setTarget(
target);
//执行代理类的方法,方法在上面
proxy
=
proxyFactoryBean.
getProxy();
}
catch (
Exception
e) {
e.
printStackTrace();
}
return
proxy;
}
return
bean;
}
}