-------android培训、java培训、期待与您交流! ----------
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下 所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简 称AOP)AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动 到原始方法的周围,这与直 接在方法中编写切面代码的运行效果是一样的, 如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技 术。
2、About 动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码(即目标方法和系统功能代码):
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
2.1、创建动态类的实例对象
1、用反射获得构造方法
2、编写一个最简单的InvocationHandler类
3、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
4、打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
5、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
Code1
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);
}
class MyInvocationHander1 implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
System.out.println(proxy1);
proxy1.clear();
Code2 用Proxy.newInstance方法直接一步就创建出代理对象
ArrayList target = new ArrayList();
Collection proxy3 = (Collection) getProxy(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
//类似一个小框架,传进两个参数,一个目标对象,一个系统任务对象
private static Object getProxy(final Object target,final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override //也可以对参数类型做过滤或者修改
//Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method); //调用系统功能
Object retVal = method.invoke(target, args); //目标对象调用方法
advice.afterMethod(method);
//System.out.println(target.toString()+" : "+target.size());
return retVal; //可以修改返回结果
}
}
);
return proxy3;
}
2.2、动态代理的工作原理图
2.3、实现AOP功能的封装与配置
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?目标、通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象
Code1--工厂类
public class 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 (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if(bean instanceof ProxyBeanFactory){
try {
Advice advice = (Advice) Class.forName(props.getProperty(name+".advice")).newInstance();
Object target = Class.forName(props.getProperty(name+".target")).newInstance();
ProxyBeanFactory pbf = ((ProxyBeanFactory)bean);
pbf.setAdvice(advice);
pbf.setTarget(target);
Object proxy =pbf.getProxy();
return proxy;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return bean;
}
}
Code2--代理工厂
public class ProxyBeanFactory {
private Object target;
private Advice advice;
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;
}
public Object getProxy() {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
// 也可以对参数类型做过滤或者修改
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
//把切面的代码用对象的方式进行封装,然后以对象的形式传递出去
advice.beforeMethod(method); // 调用系统功能
Object retVal = method.invoke(target, args); // 目标对象调用方法
advice.afterMethod(method);
// System.out.println(target.toString()+" : "+target.size());
return retVal; // 可以修改返回结果
}
});
return proxy3;
}
}
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
public class MyAdvice implements Advice {
long beginTime = 0;
@Override
public void afterMethod(Method method) {
long endTime = System.currentTimeMillis();
System.out.println(method.getName() +" runing time of "+(endTime - beginTime) +" -- end ");
}
@Override
public void beforeMethod(Method method) {
System.out.print("begin -- ");
beginTime = System.currentTimeMillis();
}
}
Code3--测试代码
InputStream ips = AopFrameworkTest.class.getResourceAsStream("configue.properties");
Object bean =new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
((Collection)bean).clear();