Spring之AOP

   1.基本概念 

      日志,安全和事务管理都是AOP可以应用的地方。

      

  应用AOP之后,日志的代码,安全的代码,事务的代码不会散落在各个模块,由Spring的AOP统一管理。如果通用的行为不满足,比如日志的级别不同,也可以自己按照需求再写一次即可。

   

  Spring中的AOP是通过动态代理来实现的,类是继承于接口的会调用JDK的动态代理,否则使用CGLIB。完成代理的动作是在运行期动态完成的。

 

   Spring AOP,AspectJ和JBoss是AOP的三个主要的框架。Spring只支持对方法进行拦截,而AspectJJBoss还支持对属性和构造函数的拦截。如果希望拦截到属性的变化,可以在Spring中引入AspectJ

    2.切点

  切点和通知是切面的最基本的元素。

  分布于应用中多处的功能被称为横切关注点,例如上图中的内容服务、计费服务等。
  切点参数的含义:
  execution(* com.spring.service.AService.*(..))
  第一个*表示任意的返回类型,com.spring.service.AService指定了一个接口,第二*表示包含接口的任意方法,..表示任意的参数类型。指定了接口AService,则对实现了接口的任意子类都包含在内。

       execution(* com.spring.service..*.*(..))

      和上面的进行比较,第一个*表示任意的返回类型,第一个..表示com.spring.service及其子包都将被拦截,*.*代表任意类的任意方法,第二个..表示任意的参数类型

      
  execution(* com.spring.service.AService.*(..)) && within(com.spring.service.impl.*)
  表示同时要是com.spring.service.impl包下面的AService接口的实现类。表达式间的操作符包括&&,||,!或者and,or,not。

  execution(!void com.spring.service.AService.*(java.lang.String,..)) ,第一个参数为String,后面可为任意个数,任意类型的参数。返回类型不为void。


  如果需要知道一个方法执行花费了多长时间,环绕通知是很合适来完成的。环绕通知还很适合于做权限控制。
  为切点增加参数信息execution(* com.spring.service.AService.*(java.lang.String)) and args(thoughts),将AService实现类方法中的String类型的thoughts作为参数传递给拦截器,可以获取方法的输入参数。

       还可以将切点写为execution(* com.spring.service.AService.*(..)),再在具体的方法上加上输入参数,如:

       @Before("anyMethod() && args(name))   //与方法的参数 名称name要一致

       public void accessCheck(String name),作用相当于取交集,先满足切点条件,再要满足输入参数是唯一的,且为String类型,类似add(String name,Integer id)的方法将不会被拦截。

      

      上面是获取方法的输入参数,下面的是获取方法的返回结果的实例:    

      @Before(pointcut = "anyMethod() ", returning = "result")   //与方法的参数 名称result要一致

      public void accessCheck(String result)

   3.通知的概念

     

   

     通知是指在执行横切关注点中的方法时,在执行动作的前,后等要执行的一些动作,如记录日志,调用事务等。

     对应于配置文件中的标签如下:

   

  4.基于XML的AOP实现

    

       aop:aspect ref ="audience",表示切点要依赖于id为audience的bean,各种通知也是在id为audience的bean中定义的。

  获取输入的参数:

     

 5.基于注解的AOP实现

     

   使用注解时@Pointcut需要依附于一个方法,方法本身不重要,可以是空实现。

      基于注解及自动扫描实现IoC和AOP。

      配置文件contextByAspect.xml的信息:

<?xml version="1.0" encoding="UTF-8"?>
<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"  
 xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop   
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
    <context:annotation-config /> 
    <!-- 自动扫描(自动注入) -->
    <context:component-scan base-package="com.spring..*"/> 
    <aop:aspectj-autoproxy/>
    
</beans>

        定义接口类:

package com.spring.service;
/**
 * 接口A
 */
public interface AService {
    
    public void fooA(String _msg);

    public void barA();
}

  定义实现类:

package com.spring.service.impl;

import org.springframework.stereotype.Component;
import com.spring.service.AService;

/**
 *接口A的实现类
 */
@Component("aService")
public class AServiceImplByAspect implements AService {	
	public AServiceImplByAspect(){
		System.out.println("a begin!");
	}
	
    public void barA() {
        System.out.println("AServiceImpl.barA()");
    }
	
    public void fooA(String _msg) {
        System.out.println("AServiceImpl.fooA(msg:"+_msg+")");
    }
}

  定义切面package com.spring.aopimport org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 *
 */
@Aspect
@Component
public class TestAspectByAspect {
	@Pointcut(value = "execution(* com.spring.service.AService.*(..))") 
	public void before(){
		
	}
	
    public void doAfter(JoinPoint jp) {
        System.out.println("log Ending method: "
                + jp.getTarget().getClass().getName() + "."
                + jp.getSignature().getName());
    }

    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long time = System.currentTimeMillis();
        Object retVal = pjp.proceed();
        time = System.currentTimeMillis() - time;
        System.out.println("process time: " + time + " ms");// if(拥有权限){
        // Object retVal = pjp.proceed();
//}else{

//
//}return retVal;
    }
    
    @Before(value = "before()")
    public void doBefore(JoinPoint jp) {
        System.out.println("log Begining method: "
                + jp.getTarget().getClass().getName() + "."
                + jp.getSignature().getName());
    }

    public void doThrowing(JoinPoint jp, Throwable ex) {
        System.out.println("method " + jp.getTarget().getClass().getName()
                + "." + jp.getSignature().getName() + " throw exception");
        System.out.println(ex.getMessage());
    }

    private void sendEx(String ex) {
        //TODO 发送短信或邮件提醒
    }
} 

  定义单元测试类:

package com.spring.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import com.spring.service.AService;
import com.spring.service.impl.BServiceImplByAspect;


public class AOPTestByAspect extends AbstractDependencyInjectionSpringContextTests {
	@Autowired(required = false)
	private  AService aService;
	

	protected String[] getConfigLocations() {
		String[] configs = new String[] { "/contextByAspect.xml"};
		return configs;
	}
	
	/**
	 * 测试正常调用
	 */
	public void testCall()
	{
		System.out.println("SpringTest JUnit test");
		aService.fooA("JUnit test fooA");
		aService.barA();
	}
	
	
	public void setAService(AService service) {
		aService = service;
	}
	
}

    SpringAOP是基于代理的,如果所需求的功能超出了Spring的代理功能,就可考虑AspectJ的使用。

   

   6.使用AOP动态的为类添加方法

      前面的都是在拦截的方法前后做一些事情,下面的配置可以动态地为接口Perfomer的实现类增加一些方法。

   

  或者进行如下的配置:

   

         

原文地址:https://www.cnblogs.com/lnlvinso/p/4001972.html