而AOP指的是面向切面编程,定义一个切面,用切面去切相应的方法,就可以织入增强的逻辑。相应的方法是指核心的业务逻辑,切面逻辑使用代理模式来实现。
关于AOP的单元测试查看SpringAOP面向切面编程功能测试
什么是代理模式?
代理代表某个真实的对象,代理提供了对真实对象另外的访问方式——即通过代理对象访问真实对象。真实对象又可称目标对象。

这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
涉及到的编程原则就是开闭原则:不要随意去修改别人已经写好的代码或者方法,如果需要修改,可以通过代理的方式来扩展该方法。
举个例子:假设我们想邀请一位明星,那么并不是直接对接明星,而是联系明星的经纪人来达到同样的目的。明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。
静态代理静态代理,简单点来说就是在程序运行之前,代理类和被代理类的关系已经确定。静态代理的实现首先要定义一个公共的接口,然后代理类和被代理类都实现这个接口,如下:
//接口public interface IUserDao { void save(); String find();}
//目标对象public class UserDao implements IUserDao{private ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();@Overridepublic void save() {System.out.println("模拟保存用户");concurrentHashMap.put("1", "张三");}@Overridepublic String find() {System.out.println("模拟查找用户");return concurrentHashMap.get("1");}}
//代理对象public class UserDaoProxy implements IUserDao{private UserDao ud = new UserDao();@Overridepublic void save() {System.out.println("代理操作,开启事务");//模拟的增强处理ud.save();System.out.println("代理操作,关闭事务");//模拟的增强处理}@Overridepublic String find() {return ud.find();}}
静态代理最大的缺点:我们得为每一个服务创建代理类,工作量太大不易管理;同时接口一旦发生改变,代理类也得相应修改。
动态代理
在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。
JDK动态代理:Java为我们创建了Proxy类,动态生成的代理类有一个共同的父类叫Proxy,我们需要告诉Proxy做什么。我们不能像静态代理那样把自己的代码放在Proxy类,因为Proxy类不是我们自己创建的。Java提供了动态处理器InvocationHandler接口。
InvocationHandler的工作是响应代理的任何调用,它是代理收到方法调用后,请求做实际工作的对象。
JDK实现动态代理,是通过Proxy的静态方法newProxyInstance来创建代理对象的,该方法有三个参数:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
ClassLoader loader:指定当前目标对象使用的类加载器
Class<?>[] interfaces:目标对象实现的接口的集合,使用泛型方式确认类型
InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的invoke方法,会把当前执行目标对象的方法作为参数传入
public void useJDKProxy() { System.out.println("-------useJDKProxy-------"); final UserDao ud = new UserDao(); // 生成代理对象 IUserDao iud = (IUserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), / 因为InvocationHandler是个接口,所以可以使用匿名内部类的方式进行实现 / new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy================"+proxy.getClass().getName());//模拟的增强处理 Object result = null; result = method.invoke(ud, args); System.out.println(method.getName()+"================"+result);//模拟的增强处理 return result; } }); iud.save(); iud.find(); }
执行结果:
-------useJDKProxy-------proxy================com.sun.proxy.$Proxy0模拟保存用户save================nullproxy================com.sun.proxy.$Proxy0模拟查找用户find================张三
从输出可以看出JDK生成的代理对象名称为:com.sun.proxy.$Proxy0,关于动态生成代理的名称,debug调用栈到Proxy.ProxyClassFactory.apply:
以下两行代码就是生成代理对象名称的源码:
CGLIB代理
private static final String proxyClassNamePrefix = "$Proxy";String proxyName = proxyPkg + proxyClassNamePrefix + num;
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,需要通过CGLib实现动态代理。
CGLib创建代理对象是通过net.sf.cglib.proxy.Enhancer实现的,代理收到方法调用后,请求做实际工作的对象是MethodInterceptor。
public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(final Object target) { this.target = target; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(this.target.getClass());//设置目标对象 enhancer.setCallback(this);//设置代理拦截器MethodInterceptor return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("obj============="+obj.getClass().getName());//模拟的增强处理 System.out.println("method============="+method.getName()); System.out.println("proxy============="+proxy.getSignature()); Object result=proxy.invokeSuper(obj, args);//目标方法 System.out.println("方法执行返回============="+result);//模拟的增强处理 return result; }}
执行结果:
--------useCGlibProxy--------obj=============proxy.staticProxy.UserDao$$EnhancerByCGLIB$$d229b515method=============saveproxy=============save()V模拟保存用户方法执行返回=============nullobj=============proxy.staticProxy.UserDao$$EnhancerByCGLIB$$d229b515method=============findproxy=============find()Ljava/lang/String;模拟查找用户方法执行返回=============张三
CGlib生成的代理对象包含一个子对象EnhancerByCGLIB字样在名称里面。debug调用栈
Enhancer.create()->Enhancer.createHelper()->AbstractClassGenerator.create(Object key)->DefaultGeneratorStrategy.generate(ClassGenerator cg)->Enhancer.generateClass()->AbstractClassGenerator.getClassName()->AbstractClassGenerator.getClassName()->DefaultNamingPolicy.getClassName(final ClassLoader loader),有一个变量bese定义了cglib代理类名称,如下:
String base = prefix + "$$" + source.substring(source.lastIndexOf('.') + 1) + getTag() + "$$" + Integer.toHexString(key.hashCode()); //变量值 prefix=proxy.staticProxy.UserDao source=net.sf.cglib.proxy.Enhancer key=proxy.staticProxy.UserDao protected String getTag() { return "ByCGLIB"; } base=proxy.staticProxy.UserDao$$EnhancerByCGLIB$$3b964c77
#程序员#