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代理!!!