为什么需要AOP?
需求加减乘除
package com.tanlei.spring.bean.Aop; public interface AtithmeticCalculator { int add(int i,int j); int sub(int i,int j); int mul(int i,int j); int div(int i,int j); }
package com.tanlei.spring.bean.Aop; public class AtithmeticCalculatorImpl implements AtithmeticCalculator{ @Override public int add(int i, int j) { System.out.println("the method add begin whith ["+i+","+j+"]"); int result=i+j; System.out.println("the method add end whith"+result); return result; } @Override public int sub(int i, int j) { System.out.println("the method sub begin whith ["+i+","+j+"]"); int result=i-j; System.out.println("the method sub end whith"+result); return result; } @Override public int mul(int i, int j) { System.out.println("the method mul begin whith ["+i+","+j+"]"); int result=i*j; System.out.println("the method mul end whith"+result); return result; } @Override public int div(int i, int j) { System.out.println("the method div begin whith ["+i+","+j+"]"); int result=i/j; System.out.println("the method div end whith"+result); return result; } }
package com.tanlei.spring.bean.Aop; public class Main { public static void main(String[] args) { AtithmeticCalculatorImpl aImpl=new AtithmeticCalculatorImpl(); aImpl.add(4, 8); aImpl.sub(4, 8); aImpl.mul(4, 8); aImpl.div(4, 8); } }
1.执行方法前后的日志
代码混乱
代码分散
使用动态代理解决上述问题
复杂动态代理代码(不推荐使用)
package com.tanlei.spring.bean.Aop; public interface AtithmeticCalculator { int add(int i,int j); int sub(int i,int j); int mul(int i,int j); int div(int i,int j); }
package com.tanlei.spring.bean.Aop; public class AtithmeticCalculatorImpl implements AtithmeticCalculator{ @Override public int add(int i, int j) { int result=i+j; return result; } @Override public int sub(int i, int j) { int result=i-j; return result; } @Override public int mul(int i, int j) { int result=i*j; return result; } @Override public int div(int i, int j) { int result=i/j; return result; } }
package com.tanlei.spring.bean.Aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class AtithmeticCalculatorLoggingProxy { // 要代理的对象 private AtithmeticCalculator target; public AtithmeticCalculatorLoggingProxy(AtithmeticCalculator target) { this.target = target; } public AtithmeticCalculator getLoggingProxy() { AtithmeticCalculator proxy = null; // 代理对象由哪一个类加载器负责 ClassLoader loader = target.getClass().getClassLoader(); // 代理对象的类型,即其中有哪些方法 Class[] interfaces = new Class[] { AtithmeticCalculator.class }; // 当调用代理对象其中的方法时,该执行的代码 InvocationHandler h = new InvocationHandler() { /** * proxy:正在返回的那个代理对象,一般情况下,在invoke方法中都不使用该对象 * method:正在被调用的方法 * args:调用方法时传人的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName=method.getName(); //日志 System.out.println("The method: "+methodName+"begins with"+Arrays.asList(args)); //执行方法 Object result=method.invoke(target, args); //日志 System.out.println("The method: "+methodName+"ends with"+result); return result; } }; proxy = (AtithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } }
package com.tanlei.spring.bean.Aop; public class Main { public static void main(String[] args) { /*AtithmeticCalculatorLoggingImpl aImpl=new AtithmeticCalculatorLoggingImpl(); aImpl.add(4, 8); aImpl.sub(4, 8); aImpl.mul(4, 8); aImpl.div(4, 8);*/ AtithmeticCalculator target=new AtithmeticCalculatorImpl(); AtithmeticCalculator proxy=new AtithmeticCalculatorLoggingProxy(target).getLoggingProxy(); int result=proxy.add(1, 2); System.out.println(result); } }
简单的方法实现动态代理(AOP)
AOP基本概念
面向切面编程
在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里;
Aop的好处
每个事物处理逻辑位于一个位置,代码不分散,便于维护和升级
业务模块更简洁,只包含核心业务代码
Spring AOP+AspectJ注解
AspectJ:java社区里最完整最流行的AOP框架
在Spring2.0版本上,可以使用基于AspectJ注解或基于XML配置的AOP
在Spring中启用AspectJ注解支持
1.导入jar包:
aopalliance-.jar
aspectj.weaver.jar
spring-aspects.jar
2.要在Spring Ioc容器中启用AspectJ注解支持,只要在bean配置文件中定义一个空的XML元素<aop:aspectj-autoproxy>
3.当Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为AspectJ切面匹配的Bean创建代理
- @Before – 方法执行前运行
- @After – 运行在方法返回结果后
- @AfterReturning – 运行在方法返回一个结果后,在拦截器返回结果。
- @AfterThrowing – 运行方法在抛出异常后,
- @Around – 围绕方法执行运行,结合以上这三个通知。
小结:
1.Spring AOP 1).加入jar包 com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aop-4.0.0.RELEASE.jar spring-aspects-4.0.0.RELEASE.jar commons-logging-1.1.1.jar spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jar 2).配置文件中加入Aop的命名空间 3)基于注解的方式 1.在配置文件中加入如下配置 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 2.把横切关注点的代码抽象到切面的类中 切面首先是一个IOC的bean,即加入@Componet注解 切面还需要加入@Aspect 注解 3.类中声明各种通知 @Before – 方法执行前运行 @After – 运行在方法返回结果后 @AfterReturning – 运行在方法返回一个结果后,在拦截器返回结果。 @AfterThrowing – 运行方法在抛出异常后, @Around – 围绕方法执行运行,结合以上这三个通知。 1.声明一个方法 2.在方法前加入@Before 3.在通知的方法里面可以加入连接点JoinPoint 访问方法的参数 如方法的签名和参数
Spring AOP 通知
package com.tanlei.spring.bean.AspectJ; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; 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.springframework.stereotype.Component; //把这个类声明一个切面:需要把该类放入到IOC容器中,再声明为一个切面 @Aspect @Component public class LoggingAspectj { // 声明该方法是一个前置通知,在目标方法开始之前执行 @Before("execution(public int com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.add(int, int))") // JoinPoint 连接点 public void beforeMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); List<Object> lists = Arrays.asList(joinPoint.getArgs()); System.out.println("The method" + methodName + " begins with " + lists); } // 声明该方法是一个 后置通知,在目标结束之后执行,无论是否发送异常 // 在后置通知中还不能访问目标方法执行的结果 @After("execution(* com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))") public void afterMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); List<Object> lists = Arrays.asList(joinPoint.getArgs()); System.out.println("The method" + methodName + " ends with" + lists); } // 返回通知 在方法正常结束执行的通知 是可以访问到方法的返回值得 @AfterReturning(pointcut = "execution(public int com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.add(int ,int))", returning = "result") public void afterReturningMethod(JoinPoint joinPoint, Object result) { System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("Method returned value is : " + result); } // 异常通知 在方法出现异常时会执行的代码 而且可以访问到异常对象,且可以指定出现特点异常时出现 @AfterThrowing(pointcut = "execution(* com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))", throwing = "error") public void logAfterThrowing(JoinPoint joinPoint, Throwable error) { System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("Exception : " + error); } // 环绕通知 围绕着方法执行 // 需要携带ProceedingJoinPoint类型的参数 // 环绕通知类似于动态代理的全过程 ProceedingJoinPoint类型的参数可以决定是否执行目标方法 // 且环绕通知必须有返回值,返回值为目标方法的返回值 @Around("execution(* com.tanlei.spring.bean.AspectJ.AtithmeticCalculator.*(int ,int))") public void logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("hijacked method : " + joinPoint.getSignature().getName()); System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs())); System.out.println("Around before is running!"); joinPoint.proceed(); // 执行目标方法 System.out.println("Around after is running!"); } }
注意点:
对于Spring AOP 采用两种代理方法,一种是常规JDK,一种是CGLIB,我的UserDaoImpl实现了一个接口IUserDao,当代理对象实现了至少一个接口时,默认使用JDK动态创建代理对象,当代理对象没有实现任何接口时,就会使用CGLIB方法。由于UserDAOImpl实现了UserDAO接口,所以强制转换必须用父类UserDAO来定义
如果你的代理对象没有实现接口的方法,就将代理对象转换成接口。
获取代理类的代码该为:
ApplicationContext ctx =
new
ClassPathXmlApplicationContext(
"applicationContext.xml"
);
UserDao userDao = (UserDaoImpl)ctx.getBean(
"userDaoImpl"
);