------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
什么是代理类
代理类是用来为原有程序的功能扩展。
程序中的代理
1>要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能。
2>编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加入系统功能的代码。
3>如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类还是代理类,这样也很容易切换。
AOP是什么?
交叉业务编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。
使用代理技术解决这个问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
》要为系统中的各个接口的类增加代理功能,那么久需要太多的代理类,全部采用静态代理方法会比较麻烦。
》JVM可以在运行期间动态的生成类的字节码,这种动态生成的类被用作代理类------即动态代理。
》JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作相同接口的目标类的代理。
》CGLIB库可动态生成一个类的子类,一个类的子类也可以用该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以用CGLIB库。
》代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下几个位置添加系统功能代码:
1>在调用目标方法之前
2>在调用目标方法之后
3>在调用目标方法前后
4>在处理目标方法异常的catch块中
实例:
创建动态类并查看方法列表信息
package day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args){
//得到代理类,参数中类加载器通常用接口的类加载器
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy1);
//希望打印出构造方法$Proxy0()
//$Proxy0(InvocationHandler,int)
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());
}
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());
}
}
}
运行结果:
练习:创建动态类实例
注意要点:①创建实例需要构造方法
②构造方法需要传入参数InvocationHandler
③InvocationHandler是一个接口,需要对其实现
package AOP;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args)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;
}
}
//通过构造方法获得代理类的实例对象
Collection myProxy=(Collection) constructor.newInstance(new MyInvocationHandler());
System.out.println(myProxy.toString());
myProxy.clear();
//这回报空指针异常是因为这个方法本应该返回一个值,而他现在只能是null,所以空指针异常。
myProxy.size();
}
}
完成InvocationHandler对象的内部功能
创建动态代理类和实例对象同步完成,那么需要一个方法newProxyInstance,该方法含有3个参数,这三个参数分别代表:
第一:加载Proxy的类加载器
第二:存放目标类的数组
第三:InvocationHandler的 实例对象
代码:
package AOP;
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 ProxyTest2 {
public static void main(String[] args)throws Exception{
Collection clazzProxy=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},//目标类时一个数组,因为可能是多个
new InvocationHandler(){//匿名内部类,实现接口。
ArrayList list=new ArrayList();//当做成员变量可被共享,因而大小是3.
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(list, args);
}
});
clazzProxy.add("abc");
clazzProxy.add("def");
clazzProxy.add("ghm");
System.out.println(clazzProxy.size());//3
}
}
分析InvocationHandler对象的运行机制
Proxy.add("abc"):实际上是把3个参数传递给了InvocationHandler的invoke做参数,其中Proxy(类),add(方法),"abd"(数组参数).最后invoke将返回一个Object值。
注意:代理类可以对参数和返回值进行修改,但类型要一样。
package AOP;
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 ProxyTest2 {
public static void main(String[] args)throws Exception{
Collection clazzProxy=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},//目标类时一个数组,因为可能是多个
new InvocationHandler(){//匿名内部类,实现接口。
ArrayList target=new ArrayList();//当做成员变量可被共享,因而大小是3.
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retVal=method.invoke(target,args);
return retVal;
//return method.invoke(Proxy,args);陷入死循环中。
}
});
System.out.println(clazzProxy.getClass().getName());//$Proxy,为什么不是ArrayList呢。
//只用toString,hashCode,equals这三个方法会交由InvocationHandler处理,其他的都有自己的实现
clazzProxy.add("abc");
clazzProxy.add("def");
clazzProxy.add("ghm");
System.out.println(clazzProxy.size());//3
}
}
分析动态类的设计原理和结构
原理解说:
客户端调用代理,代理的构造方法接收一个InvocationHandler对象,客户端调用代理的各个方法,各个方法把代理请求传递给InvocationHandler对象的invoke,InvocationHandler对象又把各个请求发给目标的相应方法。
程序最好不要硬编码,这样不利于程序的扩展。
练习: 编写一个可生成代理和加入通告的通用方法
package AOP;
import java.lang.reflect.Method;
public class MyAdvice implements Advice{
long startTime;
public void beforeMethod(Method method){
System.out.println("来了");
startTime=System.currentTimeMillis();
}
public void afterMethod(Method method){
System.out.println("我走了......");
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+"...run of... "+(endTime-startTime));
}
}
package AOP;
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 ProxyTest2 {
public static void main(String[] args)throws Exception{
final ArrayList target=new ArrayList();
Collection clazzProxy=(Collection)getproxy(target,new MyAdvice());
clazzProxy.add("abc");
clazzProxy.add("def");
clazzProxy.add("ghm");
System.out.println(clazzProxy.size());//3
}
//匿名内不类访问成员变量需要时final修饰的
private static Collection getproxy(final Object target,final Advice advice) {
Collection clazzProxy=(Collection)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 clazzProxy;
}
}
运行 结果:
实现AOP功能的封装与配置
思路:1>工厂类BeanFactory负责创建目标类或代理类的实例对象。通过配置文件实现切换。
通过getBean方法的字符串返回相应的实例对象。一种是直接返回该类对象,另一种是返回对象的getProxy方法返回的对象。
2>BeanFactory的构造方法接收一个代表文件输入流对象,配置文件。
3>ProxyFactoryBean充当封装生成动态代理的工厂,需要为工厂提供 目标和通知作为参数信息。
4>编写客户端应用:编写实现Advice接口类和配置文件配置,调用BeanFactory获取对象。
步骤:BeanFactory类通过读取配置文件得到相应的实例对象
ProxyFactoryBean类得到代理类。
AOPProxyTest类进行测试。
package AOP2;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Properties;
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 proxy=Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
//得到目标类调用invoke
MyAdvice myAdvice=new MyAdvice();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
myAdvice.beforeMethod(method);
Object retVal=method.invoke(target, args);
myAdvice.afterMethod(method);
return retVal;
}
});
return proxy;
}
}
package AOP2;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
Properties prop = new Properties();
//读取配置文件
public BeanFactory(InputStream ins) {
try {
prop.load(ins);
} catch (IOException e) {
e.printStackTrace();
}
}
//通过字符串参数得到实例对象
public Object getBean(String name) {
//通过键得到值
String className=prop.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(prop.getProperty(name+".advice")).newInstance();
Object target=Class.forName(prop.getProperty(name+".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy=proxyFactoryBean.getProxy();
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}else
return bean;
}
}
package AOP2;
import java.io.*;
import java.util.Collection;
import org.omg.CORBA.portable.InputStream;
public class AOPProxyTest {
public static void main(String[] args) {
BufferedInputStream ips = (BufferedInputStream) AOPProxyTest.class
.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
bean.getClass();
System.out.println(bean.getClass().getName());
((Collection)bean).add("ajd");
}
}
运行截图:
这就是简单的Spring原理----bean+AOP。
通过配置文件能过获得bean,也就是相应的目标类和通知。然后代理类可以给目标类增加功能。