OOP(Object oriented program):面向对象编程。
AOP(Aspect oriented program):面向方面编程。
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。
代理是实现AOP功能的核心和关键技术。只要是面向方面的编程就涉及到代理。
JVM(Java虚拟机)可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
如果目标类没有实现接口,要想生成他的代理,那么就用CGLIB库(第三方类库,不是JAVA标准),CGLIB可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么就用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
Class proxy{
void sayHello(){
......
try{
target.sayHello();
}catch(Exception e){
......
}
......
}
}
(扩展:StringBuilder与StringBuffer的区别:他们都是可变的字符序列,不同的是在单线程编程里面用StringBuilder效率要高一些,因为StringBuilder不用考虑线程安全,而多线程里面要考虑线程安全的话就使用StringBuffer);
关于动态代理类的使用见如下例子程序:
package com.heima.exam;
import java.lang.reflect.Constructor;
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 {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
System.out.println("------------begin constructors list------------");
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());
}
System.out.println("------------begin methods list------------");
Method[] methods = clazzProxy1.getMethods();
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());
}
System.out.println("------------begin create instance object------------");
class MyInvocationHandler1 implements InvocationHandler {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1 = (Collection) clazzProxy1.getConstructor(InvocationHandler.class) .newInstance(new MyInvocationHandler1());
//System.out.println(proxy1);
//proxy1.clear();
//proxy1.size();
Collection proxy2 = (Collection) clazzProxy1.getConstructor(InvocationHandler.class) .newInstance(new InvocationHandler() {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
Collection proxy3 = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[] {Collection.class},
new InvocationHandler() {
//下面定义的target就是代理目标对象,而proxy3就是代理类对象
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal = null;
long startTime = System.currentTimeMillis();
if(method.getName().equals("add") && args[0] instanceof String) {
args[0] = ((String) args[0]).replace('o', 'z');
}
retVal = method.invoke(target, args);
System.out.println(method.getName() + "方法运行所花费时间(毫秒):" +
(System.currentTimeMillis() - startTime));
return retVal;
}
});
System.out.println(proxy3.getClass().getName());
proxy3.add("kingver-hoho");
proxy3.add("hello");
proxy3.add("world");
System.out.println(proxy3.size());
System.out.println(proxy3);
/* 然而,为什么我们用代理类调用getClass()的时候返回的Class对象又是代理类的
* Class对象($Proxy0)而不是ArrayList的Class对象呢?
* 不是说代理类执行的任何方法都是交给InvocationHandler对象去调用invoke方法吗?
* 代理类也是Object的子类,会继承Object类的所有方法。
* 对Object类身上继承的方法,只对hashCode, equals和toString这三个方法进行转交
* 给 InvocationHandler对象去调用invoke方法。对于从Object类身上继承的其他方法
* 就不会进行转交给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);
}
}
*/
}
}
编写可生成代理和插入通告的通用方法(主要是传一个目标和编写实现系统功能的对象进去。)代码如下:
package com.heima.exam;
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
package com.heima.exam;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
private long startTime;
@Override
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("黑马培训毕业了");
System.out.println(method.getName() +
"方法运行所花费时间(毫秒):" +
(System.currentTimeMillis() - startTime));
}
@Override
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("黑马培训开始了");
startTime = System.currentTimeMillis();
}
}
package com.heima.exam;
import java.lang.reflect.Constructor;
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 {
private static Object getProxy(final Object target, final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal = null;
if(method.getName().equals("add") && args[0] instanceof String) {
args[0] = ((String) args[0]).replace('o', 'z');
}
advice.beforeMethod(method);
retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
});
return proxy3;
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
ArrayList target = new ArrayList();
Advice advice = new MyAdvice();
Collection proxy3 = (Collection) getProxy(target, advice);
System.out.println(proxy3.getClass().getName());
proxy3.add("kingver-hoho");
proxy3.add("hello");
proxy3.add("world");
System.out.println(proxy3.size());
System.out.println(proxy3);
}
}
使用代理就可以实现类似spring的可配置的AOP框架
(扩展:JavaBean必须要有一个不带参数的构造方法。)