动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理的方式是很麻烦的。所以JVM可以在运行时期动态的生成出类的字节码,这种动态的生成的类往往被用作代理类,即动态代理。
JVM生成的动态类必须实现一个或多个接口,因此,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和返回目标返回的结果外,还可以在代理方法中的如下四个位置添加形同功能代码。
1、再调用目标方法前
2、在调用目标方法后
3、在调用目标方法前后
4、在处理目标方法异常的catch块中。
AOP(面向方面编程)
有的业务贯穿到许多个模块中,这些业务叫做模块交叉业务。
用具体代码描述交叉业务的编程问题即为面向方面的编程(AOP),AOP的,目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法周围,这与直接在方法中编写切面代码的运行效果是一样的。
代理是实现AOP功能的核心和关键技术!
举个栗子
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Proxy;
import java.util.Properties;
public class BeanFactory
{
Properties properties = new Properties();
//BeanFactory的构造函数接收代表配置文件的输入流对象
public BeanFactory(InputStream inputStream)
{
try
{
//加载流对象
properties.load(inputStream);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public Object getBean(String name)
{
Object bean = null;
//确定要加载的类名
String className = properties.getProperty(name);
try
{
//获取加载类的字节码文件
Class clazz = Class.forName(className);
//用字节码文件创建实例对象
bean = clazz.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
if(bean instanceof ProxyFactoryBean)
{
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
Object proxy = null;
try
{
//从配置文件中获得通告和目标。
Advice advice = (Advice)Class.forName(properties.getProperty(name+".advice")).newInstance();
Object target = Class.forName(properties.getProperty(name+".target"));
//通过setAdvice和setTarget进行设置
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
}
catch (Exception e)
{
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactoryBean
{
//定义advice和target的成员变量,并获得set和get方法
private Advice advice;
private Object target;
public Object getTarget()
{
return target;
}
public void setTarget(Object target)
{
this.target = target;
}
public Advice getAdvice()
{
return advice;
}
public void setAdvice(Advice advice)
{
this.advice = advice;
}
//返回值为Object的getProxy方法
public Object getProxy()
{
//通过Proxy自身的newProxyInstance方法获得一个代理,传入三个参数分别为:
Object proxy = Proxy.newProxyInstance(
//1.目标的类加载器
target.getClass().getClassLoader(),
//2.目标类的实现的接口的集合
target.getClass().getInterfaces(),
//3.InvocationHandler子类的实例对象,此处是用的匿名内部类
new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
//在前后添加系统配置文件
advice.beforeMethod();
Object reVal = method.invoke(target, args);
advice.afterMethod();
return reVal;
}
});
return proxy;
}
}
//定义一个Advice接口
public interface Advice
{
void beforeMethod();
void afterMethod();
}
//自定义一个类实现Advice接口,并复写接口中的方法。
public class AdviceForTest implements Advice
{
public void beforeMethod()
{
System.out.println("I am before the invoked method");
}
public void afterMethod()
{
System.out.println("I am after the invoked method");
}
}
import java.io.InputStream;
//测试AOP的主类
public class TestOfAOP
{
public static void main(String[] args)
{
//建立通过相对路径读取配置文件的流对象
InputStream inputStream = TestOfAOP.class.getResourceAsStream("config.properties");
//建立BeanFactory对象传入文件读取流对象,得到bean
Object bean = new BeanFactory(inputStream).getBean("name");
//打印bean所属类的名字
System.out.println(bean.getClass().getName());
}
}
配置文件:
name=java.util.ArrayList
#name=cn.mytest.aoptest.ProxyFactoryBean
name.advice=cn.mytest.aoptest.AdviceForTest
name.target=java.util.ArrayList