AOP代理笔记
1.什么是代理?
联想 生活中的代理: 不用亲自跑去北京联想总部去买联想电脑,在本地联想代理就可以买,虽然贵了300块,但省了很多功夫和路费
且 代理 可以在 出售前后可以自己搞小动作:提高售价,搞销售活动,赠品自取等。
程序中的代理:
为 已存在的多个具有相同接口 的目标类 的各个方法 增加一些系统功能;
编写一个与 目标类具有相同接口的 代理类,代理类的每个方法调用目标类的 相同方法,并在 调用方法时加上 系统功能代码;
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是 使用目标类 还是代理类,这样以后很容易切换。譬如想要增加日志功能就配置 代理类,否则配置目标类,这样的结构增加系统功能很容易,以后运行一段时间后,又想要去掉系统功能也很容易。
2. AOP Aspect oriented program
AOP必是交叉业务(就是 不是一系列的功能,而是无关系但各个类都需要的功能:如日志,安全。这是用接口将这些功能汇集)
而你将这些功能的核心 写一遍,用到各个地方,但是想加其他功能时 用相应代理即可。
3 动态代理技术
上面说的自己编写代理 属于静态代理,
但是要为系统中各个接口增加代理功能,非常麻烦。
* JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类。即 动态代理类。
如果目标类 没有实现接口呢!
CGLIB库 可以动态生成一个类的子类,一个类的子类也可用作该类的代理,所以如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
4. java.reflect.Proxy
static Class<?> getProxy(ClassLoader loader,Class<?>... interfaces)
//每一个类 都可以 .getClassLoader()获取它的类加载器,但是现在通过 getProxy(),JVM在内存创建出了个字节码文件,内存里面加载,没有ClassLoader 怎么办呢?
通常用 和接口 相同的类加载器(可以随便指定)
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy.getName());
// 打印 $Proxy0
// 打印出 $Proxy0的构造器
Constructor[] constructors = classProxy.getConstructors();
for(Constructor constructor: constructors) {
String name =constructor.getName();
Class[] clazzParams = constructor.getParameterType();
StringBuilder sBuilder = new StringBuilder();
for(Class clazzParam : clazzParams) {
sBuilder.append(clazzParam.getName());
}
}
//打印构造器:只有这一个构造器
$Proxy(java.lang.reflect.InvocationHandler);
//知道了 构造器 ,创建它的实例对象:
clazzProxy.newInstance(); //不能用!
//先搞到 构造方法
Constructor constructor = classProxy.getConstructor(InvocationHandler.class);
//constructor.newInstance(InvocationHandler )需要InvocationHandler 对象
class MyInvocationHander1 implements InvocationHander {
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable {
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
//合并:
Collection proxy1 =(Collection)constructor.newInstance(new InvocationHander1(){
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
return null;
}
}
//获取构造器 与 newInstance()合并
Collection proxy2 =(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader();
new Class[](Collection.class);
new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable {
return null;
}
}
);
回答: 为什么
syso(proxy1.toString());
不报错?而
proxy1.size();
报空指针异常呢?
因为其实无论调用任何方法,其实都是调用 InvocationHandler接口中的 invoke()方法,而我们没有填写东西导致返回null,syso(null)还好,null转换int就会出错:空指针异常。
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{
beforeMethod();
Object retVal=method.invoke(target,args);
afterMethod();
return retVal;
}
}
);
回答: 既然是调用 InvocationHandler中的invoke()方法,实际上就还是调用目标对象的方法,那么为什么proxy3.getClass().getName(); 返回的是 $proxy0 而不是目标对象的 类名呢?
因为: 通过文档,我们知道 $Proxy0 从Object继承了很多方法,但只有 hashCode,equals,toString 委托给了Handler去干。对于其他的方法,他自己有实现。
* 编写可生成代理和插入通告的通用方法
public interface Advice {
void beforoeMethod();
void afterMethode();
}
public MyAdvice implements Advice {
void beforeMethod(){
syso("方法之前");
}
void afterMethod(){
syso("方法之后");
}
}
//黑匣子: 想要在 已经写好的类的方法中加 功能:
public static Object getProxy(Object target,Advice advice) {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
advice.beforeMethod();
Object retVal = method.invoke(target,args);
advice.afterMethod();
return retVal;
}
}
);
return proxy;
}
* 实现类似spring的可配置的AOP框架
1.工厂类BeanFactory 负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
2.BeanFactory的构造方法接收代表配置文件文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=gc.prac.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=gc.prac.MyAdvice
:
public calss 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 = clazz.newInstance();
}catch(Exception e) {
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean) {
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
Advice advice =(Advice) Class.forName(props.getProperty(name+".advice")).newInstance();
Object target = Class.forName(props.getProperty(name+".advice")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
return proxy;
}
return bean;
}
}
public class ProxyFactoryBean {
private Advice advice;
private Object target;
pubic setter,getter();
public Object getProxy(Object target,Advice advice){
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
advice.beforeMethod();
Object retVal = method.invoke(target,args);
advice.afterMethod();
return retVal;
}
});
return proxy;
}
}