1、AOP
1.1、概念
AOP 为面向切面编程,采用动态代理实现。
1.2、优点
采用动态代理的方式,可以增强原有的目标类的方法,我们可以在目标方法执行前后分别做一些事情。
对于 aop 就可以在 5 种通知里做一些事情,比如说数据库连接的释放,日志的打印,事务的操作。
这种方式,使得不用修改原有程序,就可以增加功能,降低了耦合。
1.3、结构

2、AOP 入门案例
2.1、AOP 前
AOP 采用动态代理实现,故很有必要在看 AOP 前先看动态代理的实现。动态代理分为两种,对于有接口,有实现类的可以采用 jdk 动态代理,对于没有接口,只有实现类的,需要采用 cglib 代理。
2.1.1、依赖包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <dependencies> <dependency> <artifactId>org.springframework</artifactId> <groupId>spring-core</groupId> <version>3.2.7.RELEASE</version> </dependency> <dependency> <artifactId>org.springframework</artifactId> <groupId>spring-context</groupId> <version>3.2.7.RELEASE</version> </dependency> <dependency> <artifactId>org.springframework</artifactId> <groupId>spring-beans</groupId> <version>3.2.7.RELEASE</version> </dependency> <dependency> <artifactId>org.springframework</artifactId> <groupId>spring-expression</groupId> <version>3.2.7.RELEASE</version> </dependency> <dependency> <artifactId>org.springframework</artifactId> <groupId>spring-aop</groupId> <version>3.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.aopalliance</groupId> <artifactId>com.springsource.org.aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> <version>1.1</version> </dependency>
|
2.2、jdk 动态代理(AOP 手动方式)
jdk 动态代理中,代理类需要实现目标类的所有接口。
2.2.1、目标类:接口+实现类
接口:
1 2 3 4 5
| public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); }
|
实现类:
1 2 3 4 5 6 7 8 9 10 11
| public class UserServiceImpl implements UserService { public void addUser() { System.out.println("addUser"); } public void updateUser() { System.out.println("updateUser"); } public void deleteUser() { System.out.println("deleteUser"); } }
|
2.2.2、切面类:用于保存通知
1 2 3 4 5 6 7 8
| public class MyAspect { public void before() { System.out.println("brfore"); } public void after() { System.out.println("after"); } }
|
2.2.3、代理类:生成代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class MyBeanFactory { public static UserService createService() { final UserService userService = new UserServiceImpl(); final MyAspect myAspect = new MyAspect(); 当前类.class.getClassLoader() 目标类对象.getClass().getClassLoader() 目标类对象.getClass().getInterfaces() new Class[]{接口.class} UserService proxyService = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable { myAspect.before(); Object obj = method.invoke(userService, arg2); myAspect.after(); return obj; } }); return proxyService; } }
|
2.2.4、测试类:
1 2 3 4 5 6 7
| @Test public void fun() { UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
|
2.3、cglib 代理放式
cglib 代理适用于没有接口只有实现类的情况。cglib 是通过让代理类继承目标类,从而增强目标类的方法。
2.3.1、目标类等同于 jdk 动态代理的接口的实现类
2.3.2、切面类同上
2.3.3、测试类同上
2.3.4、代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class MyBeanFactory { public static UserServiceImpl createService() { final UserServiceImpl userService = new UserServiceImpl(); final MyAspect myAspect = new MyAspect(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(userService.getClass());
enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { myAspect.before(); Object obj = arg1.invoke(userService, arg2); arg3.invokeSuper(arg0, arg2); myAspect.after(); return obj; } }); UserServiceImpl proxyService = (UserServiceImpl) enhancer.create(); return proxyService; } }
|
2.4、AOP 半自动代理
半自动代理采用 spring 提供的 org.springframework.aop.framework.ProxyFactoryBean 类(一个代理工厂)生成代理对象
2.4.1、目标类与 jdk 动态代理相同
2.4.2、切面类:环绕通知(此处与其他不同)
1 2 3 4 5 6 7 8
| public class MyAspect implements MethodInterceptor public Object invoke(MethodInvocation arg0) throws Throwable { System.out.println("before"); Object obj = arg0.proceed(); System.out.println("after"); return obj; } }
|
2.4.3、配置文件 beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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="userService" class="beanfactory.UserServiceImpl"></bean> <bean id="myAspect" class="beanfactory.MyAspect"></bean> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="beanfactory.UserService"></property> <property name="target" ref="userService"></property> <property name="interceptorNames" value="myAspect"></property> </bean> </beans>
|
2.4.4、测试类
1 2 3 4 5 6 7 8 9 10
| @Test public void fun() { String xmlPath = "beanfactory/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = (UserService) applicationContext.getBean("proxyService"); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
|
2.4.5、结果
before
addUser
after
before
updateUser
after
before
deleteUser
after
2.5、AOP 全自动代理
全自动代理仅需要在 beans.xml 中配置一下就好,使用aop:config。
spring 会自动判断使用那种代理方式,有接口时采用 jdk 方式,没有接口时采用 cglib 方式。
2.5.1、目标类与 jdk 动态代理相同
2.5.2、测试类与半自动方式相同
2.5.3、切面类
AOP 提供了 5 中通知类型,分别为,前置通知,返回通知,异常通知,最终通知,环绕通知,其中环绕通知最为强大。
除环绕通知,其他可以没有参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class MyAspect { public void before(JoinPoint joinPoint) { System.out.println("before"); } public void afterreturning(JoinPoint joinPoint, Object ret) { System.out.println("afterreturning" + ret); } public void throwable(JoinPoint joinPoint, Throwable e) { System.out.println("throwable" + " " + e.getMessage()); } public void afterafter(JoinPoint joinPoint) { System.out.println("afterafter"); } public Object invoke(ProceedingJoinPoint jontPoint) throws Throwable { System.out.println("环绕通知-------->前"); Object obj = jontPoint.proceed(); System.out.println("环绕通知---------->后"); return obj; } }
|
2.5.4、配置文件 beans.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?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"> <!-- 目标类 --> <bean id="userService" class="aop.UserServiceImpl"></bean> <!-- 切面类 --> <bean id="myAspect" class="aop.MyAspect"></bean> <!-- proxs-target-class="true":强制采用cglib方式代理 --> <aop:config proxy-target-class="true"> <!-- 切入点 --> <aop:pointcut expression="execution(* aop.UserService.*(..))" id="myPointCut" /> <!-- 切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知 --> <aop:before method="before" pointcut-ref="myPointCut" /> <!-- 返回通知 --> <aop:after-returning method="afterreturning" returning="ret" pointcut-ref="myPointCut" /> <!-- 环绕通知 --> <aop:around method="invoke" pointcut-ref="myPointCut" /> <!-- 异常通知 --> <aop:after-throwing method="throwable" throwing="e" pointcut-ref="myPointCut" /> <!-- 最终通知 --> <aop:after method="afterafter" pointcut-ref="myPointCut" /> </aop:aspect> </aop:config> </beans>
|
2.5.5、结果
因为环绕通知中有 return 语句,故执行顺序和配置有关。
2.5.5.1、配置环绕通知时
before 环绕通知——–>前 addUser
afterafter 环绕通知———->后 afterreturning1
before 环绕通知——–>前 deleteUser
afterafter 环绕通知———->后 afterreturningnull
before 环绕通知——–>前 updateUser
afterafter
throwable / by zero
2.5.5.2、不配置环绕通知
before
addUser
afterreturning1
afterafter
before
deleteUser
afterreturningnull
afterafter
before
updateUser
throwable / by zero
afterafter
2.5.5.3、其他
异常通知,只有在产生异常后才会执行,此时,返回通知会被异常挡住不在执行。
返回通知可以拿到目标类方法拿到的返回值,如果没有返回值,就是 null。
环绕通知会使得最终通知提前。类似于,return 执行前,finally 块中的会先执行。
2.5.6、补充
全自动方式也可将切面做成类似于半自动的形式。
切面类:
前置通知:
1 2 3 4 5
| public class MyAspectBefore implements MethodBeforeAdvice { public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("before"); } }
|
环绕通知:
1 2 3 4 5 6 7 8
| public class MyAspectMethod implements MethodInterceptor { public Object invoke(MethodInvocation arg0) throws Throwable { System.out.println("环绕前"); Object obj = arg0.proceed(); System.out.println("环绕后"); return obj; } }
|
配置方式:
1 2 3 4 5 6 7
| <aop:config proxy-target-class="true"> <aop:pointcut expression="execution(* aop2.UserService.*(..))" id="myPointCut" /> <aop:advisor advice-ref="myAspectMethod" pointcut-ref="myPointCut" /> <aop:advisor advice-ref="myAspectBefore" pointcut-ref="myPointCut" /> </aop:config>
|