一、代理的概念以及AOP面向方面编程
代理的概念:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理,日志,计算方法的运行时间,事务管理,等等;
编写一个与目标类具有相同接口的代里类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码,结构如下:
目标类 doSomeThing(){
业务功能代码
}
代理类 doSomeThing(){
//前置系统功能代码
目标对象.doSomeThing()
//后置系统功能代码
}
AOP面向方面编程:
如果对代理程序采用配置文件的管理方式,就不需要修改客户端程序,通过配置文件决定是目标类和代理类。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事物 日志
StudentService-------|--------|--------|-----
CourseService -------|------- |--------|-----
MiscService -------|--------|--------|-----
具体的程序代码描述交叉业务:
Method1 method2 method3
{ { {
-------------------------------------切面
…… ……. ………..
-------------------------------------切面
} } }
面向方面的编程(Aspect oriented program 简称AOP),AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术,如下所示:
-------------------------------------切面
Func1 func2 func3
{ { {
…….. …….. …….
} } }
-------------------------------------切面
二、动态代理类
动态代理的概念:JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。 JVM生成的动态类必须实现一个或多个接口, JVM生成的动态代理类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
可以在代理方法的四个位置加上系统功能代码:
1、在调用目标方法之前
2、在调用目标方法之后
3、在调用目标方法前后
4、在处理目标方法异常的catch块中
一个生成动态代理类的例子,创建动态代理,查看其所有的构造方法和普通方法:
public class ProxyTest {
public static void main(String[] args) {
/*
*
* 遍历动态类中的所有构造方法和接收参数的类型。
*/
Class clazzProxy = Proxy.getProxyClass(
Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy.getName());
//遍历所有的构造方法
Constructor[] constructors =clazzProxy.getConstructors();
for (Constructor constructor : constructors) {
String name = constructor.getName();
StringBuilder sb = new StringBuilder(name);
sb.append("(");
//将参数类型放入数组
Class[] clazzParams =constructor.getParameterTypes();
for (Class clazzParam : clazzParams) {
sb.append(clazzParam.getName()).append(",");
}
if (clazzParams != null && clazzParams.length != 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
System.out.println(sb.toString());
}
/*
*
* 遍历出动态类中的所有方法和接收参数的类型。
*/
//取出动态类的所有方法,放入数组总
Method[] methods = clazzProxy.getMethods();
//使用增强For循环遍历数组
for (Method method : methods) {
String name = method.getName();
StringBuilder sb = new StringBuilder(name);
sb.append("(");
Class[] clazzParams = method.getParameterTypes();
for (Class clazzParam : clazzParams) {
sb.append(clazzParam.getName()).append(",");
}
if (clazzParams != null && clazzParams.length != 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
System.out.println(sb.toString());
}
}
}
创建动态类的实例对象:
步骤:
1.通过反射获得动态类的构造方法。
2. 构造方法接受一个InvocationHandler对象(使用匿名内部类)。
3. 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象通过匿名类的方式传进去。
public class Test {
public static void main(String[] args) throws Exception {
//得到代理类的字节码
Class<?> clazzProxy = Proxy.getProxyClass(
Collection.class.getClassLoader(),
Collection.class);
//创建动态代理类的实例
Collection<?> proxy =
(Collection<?>)clazzProxy.getConstructor(InvocationHandler.class)
.newInstance(
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args)
throws Throwable {
return null;
}
});
System.out.println(proxy);
// 结果:null
proxy.clear();
// 执行没有返回值的方法,不会报告异常
proxy.size();
// 执行有返回值的方法,会报告异常
}
}