一、AOP的一些专业术语
* 切面(Aspect) : 横切关注点。就是交叉在各个业务逻辑中的系统服务(被模块化),类似于安全验证、事务处理、日志记录都可以理解为切面。* 织入(weaving) : 就是将切面代码插入到目标对象某个方法的过程,相当于我们在jdk动态代理里面的 invocationHandler接口方法的内容 。
* 连接点(JointPoint) : 理论上可能被切面织入的所有方法 ,比如addUser(), searchUser()的方法前、方法后或抛异常后等等。通常所有方法都可以被称为连接点。
* 切入点(PointCut) : 就是实际上被切面织入的方法。
* 目标对象(target) : 就是切入点和连接点所属的类 UserDaoImpl,被通知的对象。
* 通知(Advice) : 就是切面的实现,切面必须要完成的工作。
* 代理(Proxy) : 向目标对象应用通知之后创建的对象。
* 顾问(Advisor) : 其实就是通知的一个封装和延伸,可以将通知以更为复杂的方式织入到某些方法中
将切面织入到目标对象的连接点方法,使连接点成为切入点
AOP : 是不是就是 把通知织入到连接点的过程。
二、spring AOP 的实现
AOP开发规范:
AOP联盟为Advice定义了:org.aoplliance.aop.interface.Advicespring AOP实现了AOP联盟的规范
传统spring提供的五类Advice
前置通知org.springframework.aop.MethodBeforeAdvice
* 在目标方法执行前实施
后置通知 org.springframework.aop.AfterReturningAdvice
* 在目标方法执行后实施
环绕通知 org.aopalliance.intercept.MethodInterceptor
* 在目标方法执行前后实施
异常抛出通知 org.springframework.aop.ThrowsAdvice
* 在方法抛出异常后实施
引介通知 org.springframework.aop.IntroductionInterceptor 了解
* 在目标类中添加一些新的方法和属性
三、案列解析
首先借口和实现类是前面已经写了首先创建一个IUserDao接口,编写addUser和serchUser方法
package com.dqsy.spring.proxy;
public interface IUserDao {
public void addUser();
public void serchUser();
}
实现这个接口:
package com.dqsy.spring.proxy.impl;
import com.dqsy.spring.proxy.IUserDao;
public class IUserDaoImpl implements IUserDao {
@Override
public void addUser() {
// TODO Auto-generated method stub
/**
* 业务逻辑服务
*/
/**
* 首先进行权限的判断
*/
//System.out.println("判断权限....");
System.out.println("添加方法....");
/**
* 编写日志
*
*/
//System.out.println("编写日志");
int a = 1/0;//后面的异常通知会用到
}
@Override
public void serchUser() {
// TODO Auto-generated method stub
System.out.println("寻找方法....");
}
}
编写前置通知
package com.dqsy.spring.proxy;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
// TODO Auto-generated method stub
System.out.println("前置通知 ....在方法执行前运行");
}
}
然后编写applicationContext.xml
<!-- 配置IUserDaoImpl -->
<bean id="userDao" class="com.dqsy.spring.proxy.impl.IUserDaoImpl"></bean>
<!-- 注册前置的通知,切面的实现 -->
<bean id="beforeAdvice" class="com.dqsy.spring.proxy.MyBeforeAdvice"></bean>
<!-- 注册前置通知代理生成器, 注入目标接口,目标类,通知 -->
<bean id="myBeforeProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userDao"></property>
<property name="interfaces" value="com.dqsy.spring.proxy.IUserDao"></property>
<property name="interceptorNames" value="beforeAdvice"></property>
</bean>
编写测试文件
public class AdviceTest {
private ApplicationContext ctx=null;
@Before
public void init(){
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
//前置通知
@Test
public void beforText(){
IUserDao userDao = (IUserDao) ctx.getBean("myBeforeProxy");//代理对象
userDao.addUser();
}
结果:
后置通知:
编写MyAfterAdvice类
package com.dqsy.spring.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object retuenValue, Method paramMethod, Object[] paramArrayObject, Object targert) throws Throwable {
// TODO Auto-generated method stub
System.out.println("后置通知...在方法执行后运行");
}
}
测试类及结果:
//后置通知
@Test
public void afterText(){
IUserDao userDao = (IUserDao) ctx.getBean("myAfterProxy");//代理对象
userDao.addUser();
}
环绕方法MyAroundAdvice类:
package com.dqsy.spring.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("环绕通知...在方法前执行");
Object result = invocation.proceed();
System.out.println("环绕通知...在方法后执行");
result = false;
return result;
}
}
测试方法和结果:
package com.dqsy.spring.advice;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowingAdvice implements ThrowsAdvice {
public void afterThrowing(Exception e){
System.out.println("发生异常"+e.toString());
}
}
测试及结果:
//异常通知
@Test
public void throwText(){
IUserDao userDao = (IUserDao) ctx.getBean("myAroundProxy");//代理对象
userDao.addUser();
}
注意事项:
在编程时,应该使用 ProxyFactoryBean 创建后代理对象(UserDAOProxy ), 不要引入原来Bean (UserDAO)。
小结:
一个代理对象只能代理一个bean,意味着在实际应用中要定义多个代理(通过默认advisor自动代理生成器来解决。
从容器中获取对象是通过代理的bean的id,而不是我们在容器里面定义的目标对象的id(通过默认advisor自动代理生成器来解决)。
通知只能切入到目标类的所有方法,不能指定某些方法(通过顾问对通知的封装实现)。