使用Java来实现AOP的功能,主要介绍两种实现方法
- Proxy代理方法
- CGLib方法
1.Proxy代理
采用Proxy类方法,其基本流程为:主函数-->代理-->目标对象
对于Proxy类有一个使用前提:目标对象必须要实现接口
利用Proxy实现AOP的主要步骤如下:
- 创建接口
- 创建接口实现类
- 创建代理工厂类
接下来我们使用示例来演示
需求:学生类具有姓名属性,并具有空参数的构造函数和带参数的构造函数,获取学生的姓名属性并打印。
但是假如学生没有姓名属性的话获取学生姓名会返回Null值,为了避免这种情况的发生我们就得使用AOP的编程技巧来对getName方法进行增强。
首先我们创建一个接口StudengInterface
public interface StudentInterface { public void print(); }
接口中只有一个方法:print
接下来创建接口实现类StudentBean
public class StudentBean implements StudentInterface{ private String name; public StudentBean() { } public StudentBean(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void print() { System.out.println("Hello World!"); } }
类中具有一个String类型的属性name,并包含一个空参数的构造函数和带参数的构造函数。
接下来创建代理工厂类ProxyFactory
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory implements InvocationHandler{ private Object stu; /** * 创建目标对象的实体类 * @param stu 目标对象 * @return 目标对象实体类 */ public Object createStudentProxy(Object stu){ this.stu=stu; //返回目标对象实体类,第一个参数目标对象的类加载器,第二个参数是目标对象的接口对象, // 第三个参数是InvocationHandler默认对象。此方法会回调invoke方法 return Proxy.newProxyInstance(stu.getClass().getClassLoader(),stu.getClass().getInterfaces(),this); } /** * 处理业务逻辑: * 当Student存在名字则直接打印“Hello World” * 将如Student的名称为空,则输出相关的信息。如“名称为空,代理类已经拦截”等, * 表明代理类已经起作用了。 */ public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { StudentBean s= (StudentBean) stu; Object object=null; if (s.getName()!=null){ object=method.invoke(stu,args); }else { System.out.println("名称为空,代理类已经拦截"); } return object; } }创建对应入口类来测试代理是否生效
public class Main { public static void main(String[] args){ StudentInterface s1=new StudentBean(); // StudentInterface s1=new StudentBean("wang"); ProxyFactory factory=new ProxyFactory(); StudentInterface s2= (StudentInterface) factory.createStudentProxy(s1); s2.print(); } }
可以看到有名称的StudentBean和没有名称的StudentBean分别输出了对应的信息。
利用Proxy实现AOP功能的总结如下:
- 目标对象必须实现接口
- 返回创建的代理对象
- 重写接口的invoke()方法
- 限制条件放在invoke()方法中
CGLib官网:
https://github.com/cglib/cglib
CGlib简述
Cglib
(Code Generation Library)
是一个优秀的动态代理框架,
他是一个强大的,高性能的,高质量的Code生成类库,他可以在运行期扩展Java类与实现Java接口。
它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用的特点,它的运行速度要远远快于JDK的Proxy动态代理。
使用CGLIB需要导入以下两个jar文件:
- asm.jar – CGLIB的底层实现
- cglib.jar – CGLIB的核心jar包。
CGLIB的核心类:
- net.sf.cglib.proxy.Enhancer – 主要的增强类
- net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
- net.sf.cglib.proxy.MethodProxy – JDK的Java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
实现AOP功能步骤如下所示:
- 引入相关jar文件
- 创建实体类
- 创建CGLib代理类
- 创建入口类进行测试
示例如下:
相关的jar包以及在创建Spring工程的时候已经加载到项目中去了,
如何创建Spring项目请看: Spring学习(一)在IntelliJ IDEA上使用Maven创建Spring项目
需求和上面的一样
首先创建一个学生类StudentBean
public class StudentBean{ private String name; public StudentBean() { } public StudentBean(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void print() { System.out.println("Hello World!"); } }
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * cglib代理工厂类 * 注意实现的接口类是cglib包下的MethodInterceptor */ public class CGLibProxyFactory implements MethodInterceptor { private Object object; /** * 创建代理类对象 * @param object 被代理对象 * @return 代理类对象 */ public Object createStudent(Object object){ this.object=object; //利用Enhancer来创建代理类 Enhancer enhancer=new Enhancer(); //为目标对象指定父类 enhancer.setSuperclass(object.getClass()); //设置回调函数 enhancer.setCallback(this); //返回生成的代理类 return enhancer.create(); } /** * 业务处理逻辑代码 * 利用到了java的反射 * @param o * @param method * @param objects * @param methodProxy * @return 代理类对象 * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { StudentBean student=(StudentBean)object; Object result=null; if (student.getName()!=null){ methodProxy.invoke(object,objects);//利用反射来执行相应的方法 }else{ System.out.println("该方法已被拦截!"); } return result; } }
创建对应的入口类来测试
public class Main { public static void main(String[] args){ StudentBean student1 = (StudentBean) new CGLibProxyFactory().createStudent(new StudentBean()); StudentBean student2 = (StudentBean) new CGLibProxyFactory().createStudent(new StudentBean("wang")); student1.print(); student2.print(); } }
可以看到输出结果为:
该方法已被拦截!
Hello World!
总结:
可以看到Proxy代理方法和CGLib方法实现AOP的步骤基本相同,主要区别是:
使用Proxy代理类,他创建出来的目标对象和代理对象都必须实现相同的接口。
而CGLib方法则是直接代理目标对象,不需要实现同一接口。