AOP(Aspect Oriented Programming)也就是面向切面编程的技术。AOP基于IoC基础,是对OOP的有益补充。AOP是代码之间解耦的一 种实现。可以这样理解,面向对象编程是从静态角度考虑程序结构,面向切面编程是从动态角度考虑程序运行过程。AOP将应用系统分为两部分:
(1)核心业务逻辑(Core Business Concerns)。
(2)横向的通用逻辑,也就是所谓的切面(Crosscutting Enterprise Concerns)。
例如,所有大中型应用都要涉及的持久化管理(Persistent)、事务管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。
AOP的底层实现原理实际是Java语言的动态代理机制。AOP 代理是由AOP框架动态生成一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但代理中的方法与目标对象的方法存在差异: AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。Spring 的AOP通常和IoC配合使用,需要程序员参与的有3个部分:
(1)定义普通业务组件。
(2)定义切入点。一个切入点可以横切多个业务组件。
(3)定义增强处理。增强处理就是在AOP框架为普通业务组件织入的处理动作。
Spring有如下两种方式来定义切入点和增强处理:
(1)annotation 配置方式。使用@Aspect、 @Pointcut 等Annotation标注切入点和增强处理。
(2)xml 配置方式。使用xml配置文件定义切入点和增强处理。
我使用的是上一个学习crud操作的代码(MVC),稍作修改完成这个知识点的学习。
使用Spring实现mysql数据库的CRUD操作
(1)定义一个java文件来使用AOP技术。(LogAdvice.java(日志通知切面))
LogAdvice.java
package com.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAdvice {
//前置通知方法
public void myBeforeAdvice(JoinPoint joinPoint){
String methodname = joinPoint.getSignature().getName();
System.out.println("Before前置通知——执行"+methodname+"方法前");
}
//后置通知方法
public void myAfterAdvice(JoinPoint joinPoint){
String methodname = joinPoint.getSignature().getName();
System.out.println("执行"+methodname+"方法后——After后置通知");
}
//环绕通知方法
public void myAroundAdvice(ProceedingJoinPoint point) throws Throwable{
String methodname = point.getSignature().getName();
System.out.println("环绕通知,执行"+methodname+"方法前");
//选择执行
point.proceed();
System.out.println("环绕通知,执行"+methodname+"方法后");
}
}
(2)在applicationContext.xml文件做相关bean配置(部分代码)
<bean id="userDao" class="com.dao.UserDao"/>
<bean id="userService" class="com.service.UserService">
<property name="userDao" ref="service"></property>
</bean>
<bean id="logAdvice" class="com.advice.LogAdvice"/>
<aop:config>
<!-- 配置一个切面 -->
<aop:aspect id="logaop" ref="logAdvice" order="1">
<!-- 定义切入点,表示对service的所有方法都进行拦截 -->
<!--
expression:用来匹配执行方法的连接点
第一个*表示匹配任意的方法的返回值
第一个..表示service包及其子包
第二个*表示所有类
第三个*表示所有方法
第二个..表示方法的任意参数个数
-->
<aop:pointcut expression="execution(* com.service.*.*(..))" id="testpointcut"/>
<!-- 定义前置通知 -->
<aop:before method="myBeforeAdvice" pointcut-ref="testpointcut"/>
<!-- 定义后置通知 -->
<aop:after method="myAfterAdvice" pointcut-ref="testpointcut"/>
<!-- 定义环绕通知 -->
<aop:around method="myAroundAdvice" pointcut-ref="testpointcut"/>
</aop:aspect>
</aop:config>
</beans>
再写个测试类跑起来就可以了。
(1)AuthorityInterceptor.java(用户权限拦截器)
功能:
admin用户可以执行add()和update()方法
register用户可以执行add()方法,但不可执行update()方法
other用户都不可以执行add()和update()方法
package com.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.bean.User;
public class AuthorityInterceptor implements MethodInterceptor {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
System.out.println("拦截器==权限验证开始");
String username=this.user.getUsername();
String methodName = arg0.getMethod().getName();
if(!username.equals("admin")&&!username.equals("register")) {
System.out.println("没有任何执行权限");
return null;
}
else if(username.equals("register")&&methodName.equals("update")) {
System.out.println("register用户没有修改权限");
return null;
}
else {
Object object = arg0.proceed();
System.out.println("拦截器==权限验证结束");
return object;
}
}
}
(2)LogInterceptor.java(用户操作日志拦截器)
package com.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
String methodName = arg0.getMethod().getName();
Object object = arg0.proceed();
System.out.println("拦截器==日志记录:尝试执行"+methodName+"方法");
return object;
}
}
(3)在applicationContext.xml文件做相关bean配置(部分代码)
<bean id="admin" class="com.bean.User">
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
</bean>
<bean id="register" class="com.bean.User">
<property name="username" value="register"></property>
<property name="password" value="654321"></property>
</bean>
<bean id="other" class="com.bean.User">
<property name="username" value="other"></property>
<property name="password" value="666666"></property>
</bean>
<!-- -->
<bean id="logInterceptor" class="com.interceptor.LogInterceptor"/>
<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
<!-- admin/register/other -->
<property name="user" ref="admin"></property>
</bean>
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userDao"></property>
<property name="interceptorNames">
<list>
<value>authorityInterceptor</value>
<value>logInterceptor</value>
</list>
</property>
</bean>
测试类:
package com.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.service.UserService;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassPathXmlApplicationContext l = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = l.getBean("userService",UserService.class);
service.add();
l.close();//关闭流
service.update();
l.close();
}
}
applicationContext.xml(完整代码)
<?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:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="admin" class="com.bean.User">
<property name="username" value="admin"></property>
<property name="password" value="123"></property>
</bean>
<bean id="register" class="com.bean.User">
<property name="username" value="register"></property>
<property name="password" value="123"></property>
</bean>
<bean id="other" class="com.bean.User">
<property name="username" value="other"></property>
<property name="password" value="123"></property>
</bean>
<!-- -->
<bean id="logInterceptor" class="com.interceptor.LogInterceptor"/>
<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
<!-- admin/register/other -->
<property name="user" ref="admin"></property>
</bean>
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userDao"></property>
<property name="interceptorNames">
<list>
<value>authorityInterceptor</value>
<value>logInterceptor</value>
</list>
</property>
</bean>
<bean id="userDao" class="com.dao.UserDao"/>
<bean id="userService" class="com.service.UserService">
<property name="userDao" ref="service"></property>
</bean>
<bean id="logAdvice" class="com.advice.LogAdvice"/>
<aop:config>
<!-- 配置一个切面 -->
<aop:aspect id="logaop" ref="logAdvice" order="1">
<!-- 定义切入点,表示对service的所有方法都进行拦截 -->
<!--
expression:用来匹配执行方法的连接点
第一个*表示匹配任意的方法的返回值
第一个..表示service包及其子包
第二个*表示所有类
第三个*表示所有方法
第二个..表示方法的任意参数个数
-->
<aop:pointcut expression="execution(* com.service.*.*(..))" id="testpointcut"/>
<!-- 定义前置通知 -->
<aop:before method="myBeforeAdvice" pointcut-ref="testpointcut"/>
<!-- 定义后置通知 -->
<aop:after method="myAfterAdvice" pointcut-ref="testpointcut"/>
<!-- 定义环绕通知 -->
<aop:around method="myAroundAdvice" pointcut-ref="testpointcut"/>
</aop:aspect>
</aop:config>
</beans>
修改xml文件的联系用户id来显示不同用户下的打印结果。
<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
<!-- admin/register/other -->
<property name="user" ref="admin"></property>
</bean>