aop的概念(浅谈辄止):
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)(比如被拦截的方法)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
使用Spring进行面向切面(AOP)编程(以下是注解方式,也可以通过Xml方式):
1:首先我们要在spring的配置文件中引入aop命名空间
2:启动对@AspectJ注解的支持(蓝色部分),凡是没被拦截的方法则直接执行
3:编写切面类,下面已写的类LogPrint
4:通过配置<bean>把类交给spring管理
5:引入夹包:
核心包spring.jar输出日志信息包commons-logging.jar
如果使用了切面编程(AOP),还需要下列jar文件aspectjweaver.jar和aspectjrt.jarcglib-nodep-2.1_3.jar
配置文件:
配置文件: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy/>// 注入解析aop中使用到的注解的处理器 //通过bean让类交给Spring管理 <bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/> <bean id="log" class="cn.itcast.service.LogPrint"/> // </beans>
@Aspect //切面注解 public class LogPrint { @Pointcut(“execution(* cn.itcast.service..*.*(..))”)// 拦截切入点,(后面一句周亮总结)连接点(在spring指方法)的集合是切入点, private void anyMethod() {}//声明一个切入点 //*任意返回类型,cn.itcast.service包名,..子包,*包中的任意类,.*类中的任意方法,(..)方法参数可以有也可没有 @Before(“anyMethod() && args(userName)”)//定义前置通知,只处理参数类型为String的方法 public void doAccessCheck(String userName) { System.out.pirntln(“前置通知”); } //定义后置通知,只处理返回类型为String的方法,但是如果返回值是void的类型,也会处理, @AfterReturning(pointcut=“anyMethod()”,returning=“revalue”) public void doReturnCheck(String revalue) { // revalue存放拦截函数执行的返回值,如果返回类型是void,则revalue为null System.out.pirntln(“后置通知”); } @AfterThrowing(pointcut=“anyMethod()”, throwing=“ex”)//定义例外通知,并接受处理例外ex public void doExceptionAction(Exception ex) { System.out.pirntln(“例外通知”); } @After("anyMethod()")//定义最终通知 public void doReleaseAction() { System.out.pirntln(“最终通知”); } @Around("anyMethod()")//环绕通知 public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // 参数类型不可变 //if()判断是否有权限 System.out.pirntln(“进入方法”); Object result= pjp.proceed(); // 执行该方法才能执行目标对象的方法 System.out.println(“退出方法“); Return result; //注: pjp.proceed();会执行被拦截的业务方法,如果被拦截的页面方法还存在切面,则该方法会执行下一个切面的拦截方法再执行业务方法,如果不执行该方法,则下一个切面的方法和业务方法都不会执行。 } }
测试: ApplicationContext cxt = new ClassPathXmlApplicationContext("spring.xml"); OrderServiceBean service = cxt.getBean("orderservice"); service.save("xxx"); 控制台输出结果: 前置通知 进入方法 后置通知 最终通知 退出方法
注:可以在环绕通知解决处理其它通知的事情,从而没必要写其它通知。
public class JDKProxy implements InvocationHandler { private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject){ // 创建代理对象,前提目标类必须实现接口,即被代理的方法都是接口方法, this.targetObject = targetObject; /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法 (即该方法的返回的代理对象的方法被调用时,会让JDKProxy对象的invoke方法来执行。) */ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);// 代理类实现目标类的所有接口方法 } // 回调方法,当代理类的相应接口被执行时便执行 // 参数:代理对象,代理对象被调用的方法(拦截到的方法),被调用方法传进的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if( (String)args[0].equal(“mmm”) return method.invoke(this.targetObject, args);//把方法调用委派给目标对象 else return null; } } 当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
public class CGLIBProxy implements MethodInterceptor { private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject){// 被代理对象可以不用继承接口 this.targetObject = targetObject; Enhancer enhancer = new Enhancer();//该类用于生成代理对象 enhancer.setSuperclass(this.targetObject.getClass());//设置父类,对非final所有方法进行覆盖同时增加自身代码 enhancer.setCallback(this);//设置回调,用对象为本身 return enhancer.create(); } // 代理对象,被拦截的方法,方法参数,方法代理对象 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 环绕通知<pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java"><span style="white-space:pre"></span><pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java"><span style="white-space:pre"></span><pre code_snippet_id="358592" snippet_file_name="blog_20140522_6_5186680" name="code" class="java">Object result = null; If( args…..) { // 前置通知 advice(); Try{ result methodProxy.invoke(this.targetObject, args); //后置通知aferadvice(); }catch(Exception) { //例外通知exceptiondvice(); }finally{ //最终通知finallyadvice(); } } Return result; }
}CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。