Spring AOP(JDK动态代理)
AOP(面向切面编程):通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术(百度百科)
AOP框架:Spring AOP:使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。
AspectJ。基于Java语言的AOP框架,提供了一个专门的编译器,在编译时提供横向代码的织入。
AOP术语:
Aspect(切面):封装的用于横向插入系统的类,该类要被Spring容器识别为切面,在配置文件中通过<bean>元素指定。
Joinpoint(连接点):程序执行过程中的某个阶段点,实际上是对象的一个操作。在Spring AOP中,连接点就是指方法的调用。
Pointcut(切入点):切面与程序的交叉点,通常指类或者方法名。
Advice(通知/增强处理):AOP框架在特定的切入点执行的增强处理,可以理解为切面类中的方法,是切面的具体实现。
Target Object(目标对象):被增强的对象。
Proxy(代理):将通知应用到目标对象(Target Object)之后,动态生成的对象。
Weaving(织入):将切面代码插入到目标对象上,生成代理对象的过程。
动态代理:
所需包:
- JDK动态代理:
JDK动态代理是通过java.lang.reflect.Proxy类实现的,调用Proxy类的newProxyInstance()方法来创建对象。
接口:
package com.itheima.jdk; public interface UserDao { public void addUser(); public void deleteUser(); }
实现类:
package com.itheima.jdk; import org.springframework.stereotype.Repository; @Repository("userDao") public class UserDaoImpl implements UserDao{ @Override public void addUser() { // TODO Auto-generated method stub System.out.println("添加用户"); } @Override public void deleteUser() { // TODO Auto-generated method stub System.out.println("删除用户"); } } /* * 将该类作为目标类,对其中的方法进行增强处理 */
切面类:
package com.itheima.aspect; //切面类:可以存在多个通知Advice(即增强的方法) public class MyAspect { public void check_Permissions(){ System.out.println("模拟检查权限..."); } public void log(){ System.out.println("模拟记录日志..."); } }
代理类:
package com.itheima.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.itheima.aspect.MyAspect; /* * JDK代理类 */ public class JdkProxy implements InvocationHandler{ //声明目标接口类 private UserDao userDao; //创建代理方法 public Object createProxy(UserDao userDao){ this.userDao = userDao; //1.类加载器 ClassLoader classLoader = JdkProxy.class.getClassLoader(); //2.被代理对象实现所有的接口 Class[] clazz = userDao.getClass().getInterfaces(); //3.使用代理类,进行增强,返回的是代理后的对象 return Proxy.newProxyInstance(classLoader, clazz, this); } /* * 所有动态代理类方法的调用,都交由invoke()方法去处理 * proxy被代理后的对象 * method将要被执行的方法信息(反射) * args执行方法时需要的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //声明切面 MyAspect myAspect = new MyAspect(); //前增强 myAspect.check_Permissions(); //在目标类上调用方法,并传入参数 Object obj = method.invoke(userDao, args); //后增强 myAspect.log(); return obj; } }
该类实现类InvocationHandler接口,并且实现了invoke()方法,所有动态代理类所调用的方法都通过该方法来处理。在创建的代理方法createProxy()中,使用了Proxy 类的newProxylnstance()方法来创建代理对象。newProxylnstance()方法中包含3个参数,其中第1 个参数是当前类的类加载器,第2 个参数表示的是被代理对象实现的所有接口,第3 个参数this 代表的就是代理类JdkProxy 本身。
测试类:
package com.itheima.jdk; public class JdkTest { public static void main(String[] args) { //创建代理对象 JdkProxy jdkProxy = new JdkProxy(); //创建目标对象 UserDao userDao = new UserDaoImpl(); //从代理对象中获取增强后的目标对象 UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao); //执行方法 userDao1.addUser(); userDao1.deleteUser(); } }
创建代理对象(jdkProxy)和目标对象(userDao),然后从代理对象中获得对目标对象userDao 增强后的对象(userDao1),最后调用该对象中的添加和删除方法。
局限性:使用动态代理的对象必须实现一个或多个接口。
userDao 实例中的添加用户和删除用户的方法已被成功调用,并且在调用前后分别增加了检查权限和记录日志的功能。这种实现了接口的代理方式,就是Spring 中的JDK 动态代理。