spring AOP的核心设计思想是代理模式。
spring AOP的主要功能是日志记录、性能统计、安全控制、事务处理、异常处理等等。spring AOP 可以将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
一、AOP术语介绍:(其中连接点、切入点、切面比较重要)
1.通知(Advice): 通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。2.连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3.切入点(Pointcut):通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称, spring中允许我们方便的用正则表达式来指定
4.切面(Aspect): 通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5.引入(Introduction): 引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6.目标(Target): 即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)
7.代理(proxy): 应用通知的对象,详细内容参见设计模式里面的代理模式
8.织入(Weaving): 把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
二、前置通知
所谓前置通知就是在用户对系统进行操作之前打印日志,比如登录之前会检测登录用户。前置通知实现的是MethodBeforeAdvice接口。
示例代码:
前置通知实现类CheckUser.java:
package com.mfc.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class CheckUser implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object object) throws Throwable {
String username = (String) objects[0];
System.out.println("正在对【"+username+"】用户进行身份检测...");
}
}
模拟用户登录接口UserLogin.java:
public interface UserLogin {
public void login(String username);
}
实现用户登录接口UserLoginImpl.java:
public class UserLoginImpl implements UserLogin {
public void login(String username) {
System.out.println(username+"正在登陆系统后台...");
}
}
测试类BeforeAdviceTest.java:
package com.mfc.test;
import javax.jws.soap.SOAPBinding.Use;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mfc.advice.CheckUser;
import com.mfc.impl.UserLoginImpl;
import com.mfc.interfaces.UserLogin;
public class BeforeAdviceTest {
public static void main(String[] args) {
//方法一:直接在调用时生成代理
/*UserLogin login = new UserLoginImpl(); //具体的登录用户
BeforeAdvice advice = new CheckUser(); //前置通知
ProxyFactory factory = new ProxyFactory(); //spring代理工厂
factory.setTarget(login); //设置代理目标
factory.addAdvice(advice); //为代理目标添加前置通知
UserLogin proxy = (UserLogin) factory.getProxy(); //生成代理实例
proxy.login("孟凡诚"); //调用登陆方法
*/
//方法二:通过spring配置文件配置来声明代理
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserLogin login=(UserLogin) applicationContext.getBean("userlogin");
login.login("孟凡诚");
}
}
spring配置文件:
<bean id="checkUser" class="com.mfc.advice.CheckUser"></bean>
<bean id="target" class="com.mfc.impl.UserLoginImpl"></bean>
<!-- 使用代理工厂配置一个代理 -->
<bean id="userlogin" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定代理接口,如果是多个接口,就使用list元素指定 -->
<property name="proxyInterfaces" value="com.mfc.interfaces.UserLogin"></property>
<!-- 指定通知 -->
<property name="interceptorNames" value="checkUser"></property>
<!-- 指定目标对象 -->
<property name="target" ref="target"></property>
</bean>
三、后置通知:
所谓后置通知,就是在用户执行某一操作之后,进行日志输出。后置通知实现的是AfterReturningAdvice接口。
后置通知实现类:
public class WelcomeUser implements AfterReturningAdvice {
public void afterReturning(Object object, Method method, Object[] objects, Object target) throws Throwable {
String username = (String) objects[0];
System.out.println("欢迎您:"+username+"登陆成功!");
}
}
配置文件:
<!-- 定义通知类 -->
<bean id="welcomeUser" class="com.mfc.advice.WelcomeUser"></bean>
<!-- 定义目标类 -->
<bean id="target" class="com.mfc.impl.UserLoginImpl"></bean>
<!-- 使用spring代理工厂配置一个代理 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定代理接口,如果是多个接口,就使用list元素指定 -->
<property name="proxyInterfaces" value="com.mfc.interfaces.UserLogin"></property>
<!-- 指定通知 -->
<property name="interceptorNames" value="welcomeUser"></property>
<!-- 指定目标对象 -->
<property name="target" ref="target"></property>
</bean>
测试类:
public class AfterAdviceTest {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserLogin login = (UserLogin) applicationContext.getBean("proxyFactoryBean");
login.login("孟凡诚");
}
}
模拟用户登录的类同前置通知。
四、环绕通知:
所谓的环绕通知,就是在用户对系统进行操作前后都会输出日志。实现MethodInterceptor接口。MethodInterceptor是AOP联盟接口。
环绕通知实现类:
package com.mfc.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class CheckUserWelcome implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[] objs = invocation.getArguments(); //目标方法入参
String username = (String) objs[0]; //获取用户名
//在目标方法执行前调用
System.out.println("正在对【"+username+"】用户进行身份检测...");
//通过反射调用执行方法
Object object=invocation.proceed();
//在目标方法执行后调用
System.out.println("欢迎您:"+username+",登录成功!");
return object;
}
}
spring配置:
<bean id="checkUserWelcome" class="com.mfc.advice.CheckUserWelcome"></bean>
<bean id="target" class="com.mfc.impl.UserLoginImpl"></bean>
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mfc.interfaces.UserLogin"></property>
<property name="interceptorNames" value="checkUserWelcome"></property>
<property name="target" ref="target"></property>
</bean>
测试类:
public class BeforeAdviceTest {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserLogin login=(UserLogin) applicationContext.getBean("proxyFactoryBean");
login.login("孟凡诚");
}
}
五、异常通知:
在目标方法抛出异常时调用。实现ThrowsAdvice接口。
用户登录的接口:
public interface UserLogin {
public void login(String username);
}
实现用户登录接口的类:
public class UserLoginImpl implements UserLogin {
public void login(String username) {
if(username.equals("孟凡诚")){
System.out.println(username+"正在登陆系统后台...");
}else{
throw new RuntimeException("输入的 用户名不正确!");
}
}
}
异常通知实现类:
public class CheckUser implements ThrowsAdvice {
public void afterThrowing (Method method , Object[] objs , Object target , Exception exception){
System.out.println("Method:"+method.getName()+"抛出异常:"+exception.getMessage());
}
}
spring配置文件:
<bean id="checkUser" class="com.mfc.advice.CheckUser"></bean>
<bean id="target" class="com.mfc.impl.UserLoginImpl"></bean>
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mfc.interfaces.UserLogin"></property>
<property name="interceptorNames" value="checkUser"></property>
<property name="target" ref="target"></property>
</bean>