Spring核心:AOP
AOP(Aspect Oriented Programming面向切面编程):采用横向抽取 的方式,取代了传统的纵向体系 的重复性编码。SpringAOP采用代理模式实现了横向扩展,它不需要任何类加载器或第三方编译,自spring2.5以后,引入AspectJ作为其AOP的核心实现。AspectJ是一个开源的、专门致力于AOP的一个框架,它是java语言的扩展,提供对代码横向扩展(织入 )。
SpringAOP的底层核心(重要)
Spring如何实现“横向扩展”呢?AspectJ+ Java动态代理+Cglib代理
Java的代理模式
在现实生活中,我们八维想请刘德华做一个励志演出,那么你能不能见到其本人?不能,你见到的他的经纪人(代理人)!我们如果想请刘德华来演出,必须要和经纪人来谈,那么经纪人做什么事情呢?我们请经纪人来八维谈演出的事情(经纪人和刘德华的目标是相同的,演出的目标),但是这个经纪除了演出以外,他还要收首付款,定时间(周六上午),到周六早上,真正演出的是刘德华,是经纪人“调用”刘德华来演出的,我们尤总根本没见过刘德华本人,演出完毕后,刘德华走人,那么经纪人还要收尾款!
在以上的案例中,经纪人充当了代理人,代理人和被代理人的目标是一样的,但是代理人做的事情要比被代理做的多,我们称这个代理人对目标(被代理人)进行了增强或扩展。
静态代理
代理对象和目标对象(被代理的对象),它们有一个相同的目标,在java实现里,这个“相同的目标”就是一个接口,它们同时实现了此接口。
接口:代理对象和目标对象需要共同实现的目标方法
/**
* 这个接口是代理人和被代理需要共同实现的接口
*
* @author zhaihl
*
*/
public interface IPerformService {
/**
* 演出
*/
public void perform();
}
明星
public class StarPerform implements IPerformService {
@Override
public void perform() {
System.out.println("刘德华唱冰雨");
}
}
代理人(经纪人):对明星表演这个事情做了一个增强/扩展
/**
* 经纪人
*
* @author zhaihl
*
*/
public class AgentPerform implements IPerformService {
private StarPerform target;
public AgentPerform(StarPerform target) {
this.target = target;
}
@Override
public void perform() {
System.out.println("收首付款");
System.out.println("定时间");
System.out.println("================华丽的分隔线==================");
// 真正唱歌的是刘德华!
target.perform();
System.out.println("================华丽的分隔线==================");
System.out.println("收尾款");
}
}
邀请者
/**
* 邀请者
*
* @author zhaihl
*
*/
public class ProxyTest {
public static void main(String[] args) {
AgentPerform agent = new AgentPerform(new StarPerform());
agent.perform();
}
}
静态代理需要代理对象和目标对象共同实现同一个接口,如果接口方法发生改变,那么代理对象和目标对象的方法都将发生改变(因为它们都实现这个接口)!
动态代理
如果刘德华的经纪人没那么主动,来八维做表演由刘德华决定,而经纪人只是跟着刘德华做事,刘德华做什么事,代理就做什么事,那么这个代理没有决定权,它不需要实现接口,由刘德华实现接口(来八维做演出),经纪人只要跟着刘德华做,这种方式,我们称之为动态代理。如果接口方法发生改变,只需要刘德华一人改变,这个经纪人只是一个“跟屁虫”!
接口
/**
* 这个接口是代理人和被代理需要共同实现的接口
*
* @author zhaihl
*
*/
public interface IPerformService {
/**
* 演出
*/
public void perform();
}
明星
public class StarPerform implements IPerformService {
@Override
public void perform() {
System.out.println("刘德华唱冰雨");
}
}
经纪人:它要做的事情是跟着刘德华走的,刘德华实现什么接口,它跟着实现什么接口,那么我们说,这个经纪人要做的事情是“动态的”。
public class AgentProxy {
private StarPerform target;
public AgentProxy(StarPerform target) {
this.target = target;
}
/**
*
* @return 代理类(经纪人要做的事情)
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
// InvocationHandler:目标类所实现的接口里定义的方法操作
/**
* 就是代理类要实现的方法(自来于目标类实现的接口里的定义)
* proxy:代理类
* method代理的方法
* args:代理的方法参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收首付款");
System.out.println("定时间");
System.out.println("================华丽的分隔线==================");
// 真正唱歌的是刘德华!
method.invoke(target, args);
System.out.println("================华丽的分隔线==================");
System.out.println("收尾款");
return null;
}
});
}
调用者
/**
* 调用者
*
* @author zhaihl
*
*/
public class ProxyTest {
public static void main(String[] args) {
IPerformService agent = (IPerformService) new AgentProxy(new StarPerform()).getProxyInstance();
agent.perform();
}
}
动态创建的实现类是匿名的,我们如何拿到它实例对象呢?因为它和目标类共同实现了接口,换言之,这个类(代理类)和目标类都是接口的子类,所以我们直接将返回的对象声明其父类。
动态代理特点:代理类实现的接口是动态创建的,根据目标类来创建(目标类实现什么接口,我就是实现什么接口),动态代理和静态代理都需要实现接口,我们在编程中,如果想对没有实现任何接口的类做代理,如果实现?
Cglib代理
cglib(Code Generation Library)是一个开源的,高效的代码生成类库。cglib代理又被称为“子类”代理。它的实现方式是在内存中创建一个目标的子类,通过子类扩展父类的方法。在spring里,cglib代理方式被集成到spring核心中,我们想使用 cglib代理,只需要导入spring-core-xxx.jar
匿名子类:
public class CgProxy implements MethodInterceptor {
private StarPerform target;
public CgProxy(StarPerform target) {
this.target = target;
}
/**
* 创建目标类的匿名子类
*
* @return
*/
public Object getProxyInstance() {
// 工具类--创建匿名子类
Enhancer en = new Enhancer();
// 父类:目标类
en.setSuperclass(target.getClass());
// 设置回调
en.setCallback(this);
// 返回代理对象(目标类的子类)
return en.create();
}
/**
* 回调 proxy:代理对象 method:目标类的要做的事情(执行方法) args:目标类执行的方法参数 mp:我们要扩展的父类方法
*
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy mp) throws Throwable {
System.out.println("收首付款");
System.out.println("定时间");
System.out.println("================华丽的分隔线==================");
// 真正唱歌的是刘德华!
Object obj = method.invoke(target, args);
System.out.println("================华丽的分隔线==================");
System.out.println("收尾款");
return obj;
}
调用
public class ProxyTest {
public static void main(String[] args) {
StarPerform st = (StarPerform) new CgProxy(new StarPerform()).getProxyInstance();
st.perform();
}
}
SpringAOP的核心就是代理,那么它如何选择使用何种代理呢?
如果目标类实现了某接口,选择动态代理,如果目标类没有实现任何接口,使用cglib代理!!!