spring是一个开源框架,使用它可以降低类与类之间的耦合度,它里面最核心的两个思想是控制反转(Ioc)和切面编程(aop),首先解释一下他们两个的大致意思,尽量说的容易理解一点。
控制反转(ioc):还是拿在网上看到最多的例子来说,假如你想制造一辆汽车,如果你按照以下顺序来做的话,首先制造车轮,然后按照车轮的大小去制造底盘,然后去制造车身,车子是制造出来了,可是有一天客户说他想换一个模型,那你的底盘轮胎所有的都是不是得换,这时候的设计就是你的车身依赖底盘,底盘依赖轮胎,这就是所谓的耦合度太强了,那么你换个角度来设计,你先建立模型,然后根据模型去制造底盘然后去制造轮胎,那么你底层的设计是不是是根据你的设计来定的,而是依赖上层的代码,这就是所谓的控制反转。
切面编程(aop): 首先说一下关于aop的一些关键术语。
- Target: 目标类,需要被代理的类
- joinpoint(连接点):所谓连接点是指那些可能被拦截的方法。就是一个类里面的方法。有可能会被代理
- PointCut(切入点): 以经被增强的点。
- advice(通知/增强): 增强的代码。
- Weaving(织入): 把增强应用到目标对象来创建新的代理对象的过程。
- Aspect(切面):切入点和通知的结合
将目标类的PointCut(切入点)和Advice(增强)组合在一起形成切面,而这个过程就叫织入,然后形成代理类。
这是大致执行流程:
代理有关的知识在我的上一篇博客有解释。
代码实现如下:
/*
* jdk动态代理的一些参数:
* newProxyInstance:里面有三个参数
* 参数1:类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存
* 一般情况:当前类.class.getClassLoader
* 目标实例类.class.get
* 参数2:代理类所要实现的所有接口,只是自己类所实现的接口,如果有父类,父类也实现了某些接口,是不能获得的
*
* 参数3:处理类,是接口,一般采用匿名内部类
* 参数3_1:代理对象
* 参数3_2:代理对象当前执行方法的描述对象
* 参数3_3:方法实际参数
*/
Service beanFactory = (Service)Proxy.newProxyInstance(BeanFactory.class.getClassLoader(),
serviceImp.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object Proxy, Method method, Object[] arg2) throws Throwable {
// TODO Auto-generated method stub
aspect.befor();
Object obj = method.invoke(serviceImp, arg2); //有的方法有返回值,这里的obj就是方法的返回值
aspect.after();
return obj;
}
});
我们都知道,代理必须要有接口。
还有一种实现就是实现类没有接口的情况下,是通过创建子类来实现增强的,这就是CJLIB,字节码增强。
/*
* 采用cjlib那么它的底层就是创建目标类的子类
*/
//核心类
Enhancer enhancer = new Enhancer();
//确定父类
enhancer.setSuperclass(serviceImp.getClass());
//设置回调函数
//等效JDK的INVOcationhander
Callback callback = new MethodInterceptor()
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
aspect.befor();
//执行目标类的方法
Object obj = method.invoke(serviceImp, args);
arg3.invokeSuper(proxy, args); //执行代理类父类的方法
//执行了两次目标方法
aspect.after();
return obj;
}
};
enhancer.setCallback(callback);
return (ServiceImp)enhancer.create();
}