目录
1、AOP介绍
1.1、什么是Aop
- AOP:是Aspect Oriented Programming的缩写,意思是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
- 经典的应用:性能监视、事务管理、安全检查、缓存、日志等
- Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
- AspectJ是一个基于Java语言的AOP框架,spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了专门的编译器,在编译时提供横向代码的织入
1.2、AOP的的实现原理
- aop底层将采用代理机制进行实现
- 接口 + 实现类:spring采用的是jdk的动态代理Proxy
- 实现类:spring采用cglib字节码增强
1.3、AOP术语【掌握】
- target:目标类,需要被代理的类
- Jointpoint(连接点):是指那些可能被拦截的方法。例如目标类的所有方法
- PointCut(切入点):已经被增强的连接点。(切入点是连接点的子集)
- advice:通知 / 增强,增强的代码,例如
- weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程
- proxy(代理类):
- Aspect(切面):是切入点pointcut和通知advice· 的结合(一个线是一个特殊的面,即一个切入点和一个通知能组成一个特殊的面)
2、手动方式
2.1、JDK动态代理
- JDK动态代理是对"装饰者"设计模式的简化。使用前提:必须有接口
- jdk动态代理:
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
三个参数:
- 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存
方式1:当前类.Class().getClassLoader()
方式2:目标类实例.getClass().getClassLoader()
- 参数2 : interfaces,代理类需要实现的所有接口
方式1:目标类实例.getClass().getInterfaces()
方式2:new Class[]{目标类.Class}
- 参数3 : InvocationHandler 处理类,接口,必须进行实现,一般情况下,采用匿名内部类的方式。该接口中提供invoke方法,代理类的每一个方法执行的时,都将调用一次invoke方法,也有三个参数:
参数1:Object proxy:代理对象
参数2:Method method :代理对象当前执行的方法的描述(反射)
执行方法名:method.getName()
执行方法:method.invoke(对象 实际参数)
参数3:Object[] args:方法实际参数
2.1.1、目标类
//接口 public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); } //实现类 public class UserServiceImpl implements UserService { @Override public void addUser() { // TODO 自动生成的方法存根 System.out.println("add a user"); } @Override public void updateUser() { // TODO 自动生成的方法存根 System.out.println("upadte user"); } @Override public void deleteUser() { // TODO 自动生成的方法存根 System.out.println("delete a user"); } }
2.1.2、切面类:用于存储通知MyAspect
public class MyAspect { public void before() { System.out.println("before-method"); } public void after() { System.out.println("after-method"); } }
2.1.3、工厂类:编写工厂生成代理
public class MyBeanFactory { public static UserService createUserService() { //1、目标类 final UserService userService = new UserServiceImpl(); //2、切面类 final MyAspect myAspect = new MyAspect(); /*3、代理类:将目标类(切入点)和切面类(通知)结合--->切面 * Proxy.newProxyInstance * 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存 * 一般情况下:当前类.class.getClassLoader() * 目标类实例.getClass().getClassLoader() * 参数2:interfaces 代理类需要实现的所有接口 * 方式1:目标类实例.getClass().getInterfaces();//注意:只能获得自己的接口,不能获得父元素的接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc驱动--> DriverManager 获得接口Connection * * 参数3:InvocationHandler 处理类,接口,必须进行实现类,一般情况下,采用匿名内部类的方式 * 提供invoke方法,代理类的每一个方法执行的时候,都将调用一次invoke * 参数31:Object proxy :代理对象 * 参数32:Method method :代理对象当前执行的方法的描述(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象 实际参数) * 参数33:Object[] args : 方法实际参数 */ UserService proxService = (UserService)Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* * Object proxy:代理对象 * Method method:代理对象当前执行的方法描述 * Object[] args:方法实际参数 */ //将目标类和切面类结合 //前方法执行 System.out.println("**************************"); myAspect.before(); //执行目标类的方法 String str = method.getName(); System.out.println("当前执行的方法名是:" + str); Object obj = method.invoke(userService, args); //后方法执行 myAspect.after(); return null; } }); return proxService;//返回代理对象 } }
2.1.4、测试类:
public class TestJDK { @Test public void demo01() { UserService userService = MyBeanFactory.createUserService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }
2.1.5、测试结果
2.1.6、jdk动态代理现象(设置断点查看)
2.2、CGLIB字节码增强
- 没有接口,只能有实现类
- 采用字节码增强框架,cglib,在运行时创建目标类的子类(继承),从而对目标类进行增强
- 导入jar包
方式1:核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib-2.2.jar
依赖:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm.3.3.jar
方式2:spring-core.jar已经整合了以上两个内容
2.2.1、目标类(不需要实现接口,直接写类就行)
public class UserServiceImpl { public void addUser() { System.out.println("add a user"); } public void updateUser() { System.out.println("update a user"); } public void deleteUser() { System.out.println("delete a user"); } }
2.2.2、切面类
public class MyAspect { public void before() { System.out.println("before-method"); } public void after() { System.out.println("after-method"); } }
2.2.3、工厂类
public class MyBeanFactory { public static UserServiceImpl createUserService(){ //1.目标类 final UserServiceImpl userService = new UserServiceImpl(); //2.切面类 final MyAspect myAspect = new MyAspect(); //3.代理类 采用cglib 底层创建目标类的子类 //3.1 核心类 Enhancer enhancer = new Enhancer(); //3.2 确定父类 enhancer.setSuperclass(userService.getClass()); //3.3 设置回调函数 MethodInterceptor接口等效 jdk InvocationHandler接口 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { /* * intercept()等效于jdk中的 invoke()方法 * 参数1:代理对象 * 参数2:代理对象当前执行的方法描述(反射) * 参数3:方法实际参数 * 参数4:方法的代理, */ System.out.println("****************************"); //前方法 myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userService, args); //执行代理类的父类,执行目标类(目标类和代理类就是父子关系) methodProxy.invokeSuper(proxy, args); //后方法 myAspect.after(); return obj; } }); //3.4 创建代理 UserServiceImpl proxyService = (UserServiceImpl)enhancer.create(); return proxyService; } }
2.2.4、测试类
public class TestCglib { @Test public void demo() { UserServiceImpl userService = MyBeanFactory.createUserService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }
2.2.5、测试结果
2.2.6、cglib字节码增强现象
3、AOP联盟通知类型
- AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
- Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
1、前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
2、后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
3、环绕通知 org.aopalliance.intercept.MethodInterceptor
- 在目标方法执行前后实施增强(必须手动执行目标方法)
//环绕通知 try{ //前置方法:可以阻止目标方法的执行 //执行目标方法 //后置通知:可以获得目标方法的返回值 }catch{ //抛出异常通知 }
4、异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强(方法可以为前置、后置、环绕以及目标)
5、引介通知 org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
4、spring编写代理:半自动
- 让spring创建代理对象,从spring容器中手动的获取代理对象
- 导入jar包
核心:4+1(core、context、beans、expression)+logging
AOP:AOP联盟(规范)、spring aop(实现)
AOP联盟:
六个jar包:
4.1、目标类
public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("add a user"); } @Override public void updateUser() { System.out.println("update a user"); } @Override public void deleteUser() { System.out.println("delete a user"); } }
4.2、切面类
/* * 切面类中确定通知,需要实现不同的接口,接口就是规范,从而就确定的方法的名称 * 采用的是“环绕通知”:需要实现MethodInterceptor接口 * 环绕通知 必须手动的执行目标方法 */ public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { // TODO 自动生成的方法存根 System.out.println("前"); //手动执行目标方法:相当于通知spring执行目标方法 Object obj = mi.proceed(); System.out.println("后"); return obj; } }
4.3、配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 创建目标类 --> <bean id = "userServiceId" class = "test_b_factory_bean.UserServiceImpl"></bean> <!-- 创建切面类 --> <bean id = "myAspectId" class = "test_b_factory_bean.MyAspect"></bean> <!-- 创建代理类 *使用bean FactoryBean,底层调用getObject() 返回特殊bean *ProxyFactoryBean用于创建代理工厂bean,生成特殊的代理对象 *再在该bean中配置 *interfaces:确定接口们,通过<array>可以设置多个值,只有一个值的时候 使用value,值就是接口的全限定名 *target:确定目标类 *interceptorNames:通知切面类的名称,采用String[] 设置一个值采用的是value,设置多个值采用<array> *optimize:强制使用cglib 底层机制:如果目标类有接口,采用jdk动态代理 如果没有接口,采用cglib 如果声明是optimize = true,无论是否有接口,都采用cglib --> <bean id = "proxyUserServiceId" class = "org.springframework.aop.framework.ProxyFactoryBean"> <property name = "interfaces" value = "test_b_factory_bean.UserService"></property> <property name = "target" ref = "userServiceId"></property> <property name = "interceptorNames" value = "myAspectId"></property> <property name = "optimize" value = "true"></property> </bean> </beans>
4.4、测试类
public class TestFactoryBean { @Test public void demo() { String xmlPath = "test_b_factory_bean/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //在这里获得代理类 UserService userService = applicationContext.getBean("proxyUserServiceId",UserService.class); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }
4.5、测试结果
5、spring的AOP编程:全自动【掌握】
- 从spring容器中获得目标类,如果配置了AOP,Spring将自动的生成代理
- 要确定目标类,aspectj切入点表达式,
- 导入jar包
5.1、目标类
public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("add a user"); } @Override public void updateUser() { System.out.println("update a user"); } @Override public void deleteUser() { System.out.println("delete a user"); } }
5.2、切面类
public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { // TODO 自动生成的方法存根 System.out.println("************************"); System.out.println("前"); Object obj = mi.proceed(); System.out.println("后"); return obj; } }
5.3、配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1、目标类 --> <bean id = "userServiceId" class = "test_c_spring_aop.UserServiceImpl"></bean> <!-- 2、切面类 --> <bean id = "myAspectId" class = "test_c_spring_aop.MyAspect"></bean> <!-- 3、aop 3.1 导入命名空间 3.2 使用<aop:config>进行配置 proxy-target-class = "true":声明式使用cglib代理 有接口是jdk动态代理,没有接口cglib代理 <aop:pointcut>:切入点,从目标对象获得具体方法 <aop:advisor>:特殊的切面,只有一个通知和一个切入点 advice-ref : 通知引用(通知在切面类中) pointcut-ref :切入点引用 3.3 切入点表达式 execution(* test_c_spring_aop. *. * (...)) 选择方法 返回值任意 包 类名任意 方法名任意 参数任意 --> <aop:config proxy-target-class = "true"> <aop:pointcut expression="execution(* test_c_spring_aop.*.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/> </aop:config> </beans>
5.4、测试类
public class TestSpringAop { @Test public void demo() { String xmlPath = "test_c_spring_aop/applicationContext.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = (UserService) applicationContext.getBean("userServiceId"); userService.addUser(); userService.updateUser(); userService.deleteUser(); } }