AOP,Aspect Oriented Programming,意为面向切面编程,是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。一般应用于事物管理,性能监视,安全检查,缓存,日志等。
-
AOP实现原理
AOP底层是采用代理的机制进行实现。如果是一个实现类(接口的实现类),Spring会采用jdk的动态代理Proxy。如果只是一个类,Spring会采用cglib字节码增强。
关于动态代理可以参考我的另一篇博文:https://www.cnblogs.com/chichung/p/10358444.html
1.动态代理的方式
目标类:
切面类:
工厂类:
2.cglib增强字节码的方式
先导入jar包:
核心:hibernate-distribution-3.6.10.Finallibytecodecglibcglib-2.2.jar
依赖:struts-2.3.15.3appsstruts2-blankWEB-INFlibasm-3.3.jar
注意:spring-core.jar已经整合以上两个内容了,现在只不过是我们自己实现原理才使用的。
目标类和切面类写法一样,工厂类:
-
Spring编写半自动代理
导包:Spring核心4+1依赖包,AOP联盟(规范,是一个依赖包,com.springsource.org.aopalliance)+Spring-aop(实现,aop包)
(1)目标类
public interface UserService { public abstract void addNewUser(); }
public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void addNewUser() { // 业务逻辑操作 System.out.println("addNewUser的操作"); } }
(2)切面类
public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("执行方法前的代码"); Object obj = mi.proceed(); System.out.println("执行方法后的代码"); return obj; } }
(3)Spring配置
<?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:context = "http://www.springframework.org/schema/context" xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--业务类--> <bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean> <!--切面类--> <bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean> <!--使用工厂bean创建代理--> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.chichung.service.UserService"></property> <property name="target" ref="userService"></property> <property name="interceptorNames" value="myAspect"></property> </bean> </beans>
默认采用的是jdk动态代理原理,如果使用cglib的原理创建代理,加上:
<property name="optimize" value="true"></property>
(4)测试类
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) context.getBean("proxyService"); userService.addNewUser(); } }
-
Spring AOP全自动编程
导包:Spring核心4+1依赖包,AOP联盟(规范,是一个依赖包,com.springsource.org.aopalliance)+Spring-aop(实现,aop包)+
spring-framework-3.0.2.RELEASE-dependenciesorg.aspectjcom.springsource.org.aspectj.weaver1.6.8.RELEASE(依赖包)
(1)目标类:
public interface UserService { public abstract void addNewUser(); }
public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void addNewUser() { // 业务逻辑操作 System.out.println("addNewUser的操作"); } }
(2)切面类:
public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("执行方法前的代码"); Object obj = mi.proceed(); System.out.println("执行方法后的代码"); return obj; } }
(3)Spring配置
(4)测试类:
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) context.getBean("userService"); userService.addNewUser(); } }
-
重点:通过AspectJ实现Spring AOP
AspectJ是一个基于Java语言的AOP框架。Spring2.0以后新增了对AspectJ切点表达式支持。新版本的Spring框架,建议使用AspectJ方式来开发AOP。
导包:
aopalliance | AOP联盟规范,spring-aop的依赖包 |
spring-aop | AOP实现 |
aspectj.weaver | AspectJ规范,spring-aspects的依赖包 |
spring-aspects | AspectJ实现 |
1.AspectJ的通知类型
before:前置通知(应用:各种校验) 在方法执行前执行,如果通知抛出异常,阻止方法运行 |
afterReturning:后置通知(应用:常规数据处理) 方法正常返回后执行,如果方法中抛出异常,通知无法执行 必须在方法执行后才执行,所以可以获得方法的返回值。 |
around:环绕通知(应用:十分多,例如事务) 方法执行前后分别执行,可以阻止方法的执行 必须手动执行目标方法 |
afterThrowing:抛出异常通知(应用:包装异常信息) 方法抛出异常后执行,如果方法没有抛出异常,无法执行 |
after:最终通知(应用:清理现场) 方法执行完毕后执行,无论方法中是否出现异常 |
2.切入点表达式
execution() 用于描述方法
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略。
public 公共方法
* 任意
返回值,不能省略。
void 返回没有值
String 返回值字符串。
* 任意
包
com.gyf.crm 固定包
com.gyf.crm.*.service crm包下面子包任意 (例如:com.gyf.crm.staff.service)
com.gyf.crm.. crm包下面的所有子包(含自己)
类
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
throws ,可省略,一般不写。
下面的是很少用或者基本不用的:
within:
匹配包或子包中的方法
this:
匹配实现接口的代理对象中的方法
target:
匹配实现接口的目标对象中的方法
args:
匹配参数格式符合标准的方法
bean(id)
对指定的bean所有的方法
3.AspectJ案例(基于xml配置)
(1)before前置通知、afterReturning后置通知、afterThrowing抛出异常通知、after最终通知的XML配置。
目标类:
public interface UserService { public abstract void addNewUser(); }
public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void addNewUser() { // 业务逻辑操作 System.out.println("addNewUser的操作"); } }
切面类(不用再实现MethodInterceptor接口)
public class MyAspect{ public void myBefore(){ System.out.println("执行方法前的代码"); } public void myAfter(){ System.out.println("执行方法后的代码"); } }
Spring配置
<?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:context = "http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--业务类-->
<bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean>
<!--切面类-->
<bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean>
<!--proxy-target-class="true"表示用cglib字节码增强方式来实现--> <!--proxy-target-class="false"表示用动态代理的方式来实现--> <aop:config proxy-target-class="true"> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointCut" expression="execution(* com.chichung.service.impl.*.*(..))"></aop:pointcut> <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before> <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after> </aop:aspect> </aop:config> </beans>
测试类:
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) context.getBean("userService"); userService.addNewUser(); } }
(2)around环绕通知的XML配置(与前置通知后置通知稍微有点不同)
目标类跟上面都是一样。
切面类:
public class MyAspect{ public Object myAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("执行方法前的代码"); Object obj = pjp.proceed(); System.out.println("执行方法后的代码"); return obj; } }
Spring配置:
<?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:context = "http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--业务类--> <bean id="userService" class="com.chichung.service.impl.UserServiceImpl"></bean> <!--切面类--> <bean id="myAspect" class="com.chichung.aspect.MyAspect"></bean> <!--proxy-target-class="true"表示用cglib字节码增强方式来实现--> <!--proxy-target-class="false"表示用动态代理的方式来实现--> <aop:config proxy-target-class="true"> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointCut" expression="execution(* com.chichung.service.impl.*.*(..))"></aop:pointcut> <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around> </aop:aspect> </aop:config> </beans>
测试类和上面的示例一样。
(3)总结:
3.AspectJ案例(基于注解配置)
(1)声明使用注解
<?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:context = "http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启注解--> <context:annotation-config></context:annotation-config> <!--扫描注解--> <context:component-scan base-package="com.chichung"></context:component-scan> <!--确定AOP注解生效--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
(2)替换service和切面bean
@Component("userService") public class UserServiceImpl implements UserService { private UserDao userDao; @Override public void addNewUser() { // 业务逻辑操作 System.out.println("addNewUser的操作"); } }
@Component("myAspect") public class MyAspect{ ...
(3)声明切面
@Component("myAspect") @Aspect public class MyAspect{ ...
<!--声明切面--> <aop:config> <aop:aspect ref="myAspect"></aop:aspect> </aop:config>
(4)声明公共切入点
@Component("myAspect") @Aspect public class MyAspect{ @Pointcut("execution(* com.chichung.service.impl.*.*(..))") public void myPointCut(){}; ...
(5)声明环绕通知(其他通知也类似,不一一举例了)
@Component("myAspect") @Aspect public class MyAspect{ @Pointcut("execution(* com.chichung.service.impl.*.*(..))") public void myPointCut(){}; @Around("myPointCut()") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("执行方法前的代码"); Object obj = pjp.proceed(); System.out.println("执行方法后的代码"); return obj; } }
(6)测试类与之前的一样。