目录
一、前言
在讲Spring的AOP之前我们先说说原来我们处理事务应该在一个层完成,它是应该在Dao(数据访问层)完成还是在Service(业务层)完成?
很显然我们应该是在业务层完成的,例如银行转账这是一个事务,它只有当一方完成转账,一方完成收账这样才能说一个事务完成,如果出现特殊情况导致我们的事务未能完成那么就必须实现回滚,恢复原来的状态,但是像原来一样在业务层完成这样的公共操作是不是很容易导致冗余,所以Spring的AOP就是想把所有共用代码都剥离出来,单独放置到某个类中进行集中管理,在具体运行时,由容器进行动态织入这些公共代码。
我们从官方的解释:AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
所以接下来我们用一个动态代理来实现一下怎么将公共代码剥离
代理类
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.junit.Test;
import com.DJX.Dao.UserDao;
import com.DJX.Dao.Impl.UserDaoImpl;
public class UserProxyTest {
@Test
public void testProxy() {
UserDaoImpl userDaoImpl = new UserDaoImpl();
Class[]clazz=userDaoImpl.getClass().getInterfaces();
System.out.println(Arrays.toString(clazz));
UserDao userdao = (UserDao) Proxy.newProxyInstance(UserProxyTest.class.getClassLoader(), clazz, new MyHandeler(userDaoImpl));
/*
Classes获取对象的接口个数
NewProxyInstance创建代理,三个参数分别是类加载器,接口,实现接口InvocationHandler
*/
System.out.println(userdao.hashCode());
userdao.add();
int res =userdao.addUser("张三");
//每次当调用方法的时候,才会调用代理的invoke,并且addUser就是method,"张三"就是args,而此时的MyHandeler就像一个监控器一样时刻监控请求代理对象。
System.out.println(res);
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class MyHandeler implements InvocationHandler{
Object target;
public MyHandeler(Object target) {
// TODO Auto-generated constructor stub
this.target=target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println(method+"\t"+Arrays.toString(args));
Object res = method.invoke(target, args);
return res;
}
}
画个图帮助理解
二、AOP中的几个概念
1、连接点:类里面那些方法可以被增强,这些方法称为连接点(@pointcut就是连接点)
2、切入点:实际被增强的方法,就被称为切入点
如execution(* com.DJX.Dao.*.*(..))
第一部分*号表示任意返回值
第二部分*号com.DJX.Dao.*表示在com.DJX.Dao包下的任何类
第三部分*号表示任何类下的任何方法
第四部分(..)表示任意参数个数
3、通知:所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
4、切面:动作,把通知应用到切入点的过程,利用的是@aspect类来实现的
三、三种方式实现通知的五个类型
1、注解实现
先要对xml的文件进行设置扫描基础包,同时要设置 <aop:aspectj-autoproxy>否则注解不产生效果
<context:component-scan base-package="com.DJX"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
数据链路层书写方法交给Spring管理
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void add() {
System.out.println("UserDao的add方法");
}
public void save() {
System.out.println("UserDao的save()方法");
// System.out.println(1/0);
}
}
需要代理类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component//基本类
@Aspect
public class UserProxy {
@Before(value="execution(* com.DJX.Dao.UserDao.save(..))")
//注解在前执行*代表有没有返回,com.DJX.Dao.UserDao包名,save方法,(..)参数
public void before() {
System.out.println("方法执行之前。。。。");
}
@After(value="execution(* com.DJX.Dao.UserDao.save(..))")//后置通知
public void after() {
System.out.println("方法执行之后。。。。");
}
@AfterReturning(value="execution(* com.DJX.Dao.UserDao.save(..))")//最终通知
public