1 反射

反射是Java语言的一个特性,它允许程序在运行时来进行自我检查并对内部的成员进行操作,在java中,只要给定类的名字,那么就可以通过反射机制来获取类的全部信息。

反射的作用

  • 在运行时判断任意一个对象所属的类
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 在运行时构造任意一个类的对象

1.1 Class类

java.long.Class类是Java反射机制的基础,它用于封装被装入到JVM中的类(包括类和接口)的信息,当一个类或接口被装入到JVM时便会产生一个与之对应的Class对象,可以通过这个对象对被装入类的详细信息进行访问;
虚拟机为每种类型管理一个独一无二的Class对象,也就是说每个类都对应一个Class对象。运行程序时,JVM首先会检查要加载的对象所对应的Class对象是否已经加载,如果没有加载,JVM就会根据类型查找对应的.class文件,并将其Class对象载入。
获取Class对象的方法
1.Object.getClass()——仅限于引用类型
2.类名.class

Integer.class.newInstance();
int.class.newInstance();

3.Class.forName()——仅限于引用类型

Class<?> c = Class.forName("java.long.String")

Class的修饰符Modifier
java.long.reflect.Modifier提供对Class修饰符的解码,可以使用Class.getModifiers()获得调用类的修饰符的二进制值,然后使用Modifier.toString(int modifiers)将二进制值转换为字符串:

Class<?> c = Class.forName("java.lang.String");
int modifiers = c.getModifiers();
System.out.println(modifiers);
String str = Modifier.toString(modifiers);
System.out.println(str);

Class的成员Member
java.lang.reflect.Member代表Class的成员,每个成员都有类型,分为是否从父类继承,以及是否允许直接访问,Member有3个实现类:

  1. java.long.reflect.Constructor:表示该Class的构造函数
  2. java.long.reflect.Field:表示该Class的成员变量
  3. java.long.reflect.Method:表示该Class的成员方法

1.2 Field类

待续

1.3 Method类

反射方法调用

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }
  • Object obj——调用方法的对象,静态方法可直接传入null;
  • Object… args——该方法的参数;

2 静态代理

静态代理是指由程序员自己编写,在编译期就确定好代理关系的一种代理方式。

首先确定目标接口及其实现类:

public interface SayService{
	void say(String str);
}
public class SayServiceImpl implements SayService{
	@Override
	public void say(String str){
		System.out.println("目标类执行say()方法:" + str);
	}
}

代理类实现目标接口:

public class ProxyTest implements SayService{
	private SayServiceImpl target;
	public ProxyTest(SayServiceImpl target){
		this.target = target;
	}
	@Override
	public void say(String str){
		System.out.println("代理类执行say()方法:" + str);
		target.say(str);
	}
}

测试静态代理:

public class Main{
	public static void main(String[] args){
		SayService target = new SayServiceImpl();
        ProxyTest proxy = new ProxyTest(target);
        proxy.say("haha");
	}
}

静态代理的作用:

  1. 控制真实对象的访问权限:通过代理对象控制真实对象的使用权限;
  2. 避免创建大对象:通过使用一个代理小对象来代表一个真实的大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度;
  3. 增强真实对象的功能:这个比较简单,通过代理可以在调用真实对象的方法的前后增加额外功能。

3 动态代理

动态代理是在程序运行过程中通过反射机制动态的生成代理对象,完成特定的功能,常用的动态代理有一下两种:

  • JDK动态代理——基于实现接口的方式;
  • CGLIB动态代理——通过继承实现。

3.1 JDK动态代理

动态代理步骤

  1. 获取RealSubject上的所有接口列表;
  2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
  3. 根据要实现的接口信息,在代码中动态创建该Proxy类的字节码;
  4. 将对应的字节码转换为对应的class对象;
  5. 创建InvocationHandler实例handler,用来处理Proxy所有方法调用;
  6. Proxy的class对象以创建的handler对象为参数,实例化一个Proxy对象。

InvocationHandler接口
InvocationHandler接口定义:

public interface InvocationHandler{
	public Object invoke(Proxy proxy,Method method,Object[] args) 
		throws Throwable;
}

每个代理类的实例都需要关联到一个handler,当我们通过代理对象调用目标方法时,这个方法的调用就会被转发为InvocationHandler接口的invoke()方法的调用,invoke()方法的参数说明:

  • proxy——生成的代理对象
  • method——要代理的真实对象的目标方法
  • args——调用目标方法时接收的参数

Proxy类
Proxy类的作用就是根据目标类来创建一个动态代理类,常用的是newProxyInstance()方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHanlder h) throws IllegalArgumentException

参数说明:

  • loader——一个类加载器对象,定义了由哪一个类加载器对象来对生成的代理对象进行加载;
  • interfaces——一个接口对象数组,表示需要给代理对象提供什么样的接口从而来实现代理方法;
  • h——一个InvocationHandl对象,表示当这个代理对象调用方法时会关联到哪一个handler对象。

动态代理实例
创建目标接口及其实现类:

public interface DoSomethingService {
    void say(String str);
    String cry();
}

public class DsServiceImpl implements DoSomethingService {
   @Override
   public void say(String str) {
       System.out.println("目标类执行say()方法:" + str);
   }

   @Override
   public String cry() {
       System.out.println("目标类执行cry()方法:");
       return "wuwu";
   }
}

创建一个InvocationHandler接口的实现类:

public class DsInvocation implements InvocationHandler {
   //要代理的真实对象
   private Object target;

   //通过构造方法将真实对象赋值
   public DsInvocation(Object target) {
       this.target = target;
   }

   /**
    * 
    * @param proxy 代理对象
    * @param method 要执行的目标方法
    * @param args 目标方法的参数
    * @return
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       Object result = null;
       if ("say".equals(method.getName())){
           //前置处理
           System.out.println("————begin_say————");
           //调用真实对象的目标方法
           result = method.invoke(target,args);
           //后置处理
           System.out.println("————end_say————");
       }else {
           System.out.println("————begin_cry————");
           result = method.invoke(target,args);
           System.out.println("————end_cry————");
       }
       return result;
   }
}

测试动态代理:

public class TestMain {
   public static void main(String[] args) {
       DoSomethingService target = new DsServiceImpl();
       DsInvocation invocation = new DsInvocation(target);
       DoSomethingService proxy = (DoSomethingService)Proxy.newProxyInstance(
   target.getClass().getClassLoader(),//类加载器
   target.getClass().getInterfaces(),//目标接口
   invocation//Invocation对象
   );
       proxy.say("haha");
       //输出代理类的类名
       System.out.println(proxy.getClass().getName());
       String result = proxy.cry();
       System.out.println(result);
   }
}

测试结果:

————begin_say————
目标类执行say()方法:haha
————end_say————
com.sun.proxy.$Proxy0
————begin_cry————
目标类执行cry()方法:
————end_cry————
wuwu

com.sun.proxy.$Proxy0

通过 Proxy.newProxyInstance 创建的代理对象是在 jvm 运行时动态生成的一个对象,它并不是 InvocationHandler 类型,也不是定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式——以$开头,Proxy 为中,最后一个数字表示对象的标号。

3.2 CGLIB动态代理

CGLIB与JDK动态代理要求被代理类必须实现接口不同,它是通过让代理类继承被代理类,从而在代理类中对代理方法进行增强操作。
CGLIB动态代理实例
首先创建被代理类:

public class SayService{
    public void saySomething(){
        System.out.println("执行SayService:saySomething()方法");
    }
}

自定义MyInterceptor继承MethodInterceptor接口:

public class MyIntercepter implements MethodInterceptor {
    /**
     *
     * @param o cglib生成的代理对象
     * @param method 目标对象方法
     * @param objects 代理方法的参数
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before:" + method.getName());
        Object obj = methodProxy.invokeSuper(o,objects);
        System.out.println("after:" + method.getName());
        return obj;
    }
}

生成CGLIB代理对象调用目标方法:

public class CglibMain {
    public static void main(String[] args) {
        //创建一个Enhancer对象
        Enhancer enhancer = new Enhancer();
        //设置其父类为目标类
        enhancer.setSuperclass(SayService.class);
        //设置enhancer的回调对象
        enhancer.setCallback(new MyIntercepter());
        //创建代理对象
        SayService proxy = (SayService)enhancer.create();
        //通过代理对象执行代理方法
        proxy.saySomething();
    }
}

//output
//before:saySomething
//执行SayService:saySomething()方法
//after:saySomething

4 面向切面编程

4.1 基于AspectJ实现

案例实现

定义目标接口及实现类

//接口类
public interface UserDao {

    int addUser();

}

//实现类
@Repository
public class UserDaoImp implements UserDao {
    @Override
    public int addUser() {
        System.out.println("add user ......");
        return 6666;
    }
}

编写切面类

@Aspect
public class MyAspect {

    /**
     * 前置通知
     */
    @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 后置通知
     * returnVal,切点方法执行后的返回值
     */
    @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
    public void AfterReturning(Object returnVal){
        System.out.println("后置通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
        System.out.println("出现异常:msg="+e.getMessage());
    }

    /**
     * 最终通知
     */
    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void after(){
        System.out.println("最终通知....");
    }
}

XML配置文件

<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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 启动@aspectj的自动代理支持-->
    <aop:aspectj-autoproxy />

    <!-- 定义目标对象 -->
    <bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
    <!-- 定义aspect类 -->
    <bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>

启动测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:spring/spring-aspectj.xml")
public class UserDaoAspectJ {
    @Autowired
    UserDao userDao;

    @Test
    public void aspectJTest(){
        userDao.addUser();
    }
}

除了上述方式外,还可采用pointcut注解定义切点,如下所示:

/**
 * 使用Pointcut定义切点
 */
@Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
private void myPointcut(){}

/**
 * 应用切入点函数
 */
@After(value="myPointcut()")
public void afterDemo(){
    System.out.println("最终通知....");
}

切入点指示符
SpringAOP提供了匹配表达式,用来将通知方法过滤到相应的目标方法上,这些表达式也叫切入点指示符。
通配符
在定义匹配表达式时,通配符几乎随处可见,如*、… 、+ ,它们的含义如下:

  • … :匹配方法定义中任意数量的参数,还可匹配类定义中的任意数量的包
//任意返回值,任意名称,任意参数的公共方法
execution(public * *(..))
//匹配com.zejian.dao包及其子包中所有类中的所有方法
within(com.zejian.dao..*)
  • *:匹配任意数量的字符
//匹配com.zejian.service包及其子包中所有类的所有方法
within(com.zejian.service..*)
//匹配以set开头,参数为int类型,任意返回值的方法
execution(* set*(int))
  • +:匹配给定类的任意子类
//匹配实现了DaoUser接口的所有子类的方法
within(com.zejian.dao.DaoUser+)

类型签名表达式
为了方便类型(如接口、类名、包名)过滤方法,Spring AOP 提供了within关键字。其语法格式如下:

within(<type name>)

type name使用类名或者包名替换即可,案例:

//匹配com.zejian.dao包及其子包中所有类中的所有方法
@Pointcut("within(com.zejian.dao..*)")

//匹配UserDaoImpl类中所有方法
@Pointcut("within(com.zejian.dao.UserDaoImpl)")

//匹配UserDaoImpl类及其子类中所有方法
@Pointcut("within(com.zejian.dao.UserDaoImpl+)")

//匹配所有实现UserDao接口的类的所有方法
@Pointcut("within(com.zejian.dao.UserDao+)")

方法签名表达式
我们同样可以使用execution关键字根据方法签名对目标方法进行过滤,语法如下:

//scope :方法作用域,如public,private,protect
//returnt-type:方法返回值类型
//fully-qualified-class-name:方法所在类的完全限定名称
//parameters 方法参数
execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters))

可根据方法作用域、返回值类型、所在类的全限定名及参数类型进行方法的过滤,案例:

//匹配UserDaoImpl类中的所有方法
@Pointcut("execution(* com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中的所有公共方法并且返回值为int类型
@Pointcut("execution(public int com.zejian.dao.UserDaoImpl.*(..))")

//匹配UserDaoImpl类中第一个参数为int类型的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(int , ..))")

其他指示符

  • bean:用于匹配特定名称的Bean对象的方法
//匹配名称中带有后缀Service的Bean。
@Pointcut("bean(*Service)")
private void myPointcut1(){}
  • this:用于匹配当前AOP代理对象类型的执行方法
//匹配了任意实现了UserDao接口的代理对象的方法进行过滤
@Pointcut("this(com.zejian.spring.springAop.dao.UserDao)")
private void myPointcut2(){}
  • target:用于匹配任意实现了当前对象类型的执行方法
//匹配了任意实现了UserDao接口的目标对象的方法进行过滤
@Pointcut("target(com.zejian.spring.springAop.dao.UserDao)")
private void myPointcut3(){}
  • @within:用于匹配所有持有指定注解类型内的方法,注意与类型签名表达式within的区别
//匹配使用了MarkerAnnotation注解的类(注意是类)
@Pointcut("@within(com.zejian.spring.annotation.MarkerAnnotation)")
private void myPointcut4(){}
  • @annotation:根据方法所应用的注解进行过滤
//匹配使用了MarkerAnnotation注解的方法(注意是方法)
@Pointcut("@annotation(com.zejian.spring.annotation.MarkerAnnotation)")
private void myPointcut5(){}

5种通知方法

  • 前置通知@Brfore

该通知在目标函数执行前执行,JoinPoint是Spring提供的静态变量,可以获取目标对象的信息,如类名称,方法参数,方法名称等,该参数是可选的。

/**
 * 前置通知
 * @param joinPoint 该参数可以获取目标对象的信息,如类名称,方法参数,方法名称等
 */
@Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
public void before(JoinPoint joinPoint){
    System.out.println("我是前置通知");
}
  • 后置通知@AfterReturning

该通知在目标函数执行完成后执行,并可以获取到目标函数最终的返回值returnVal,当目标函数没有返回值时,returnVal将返回null,必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意,在任何通知中这些参数都是可选的,需要使用时直接填写即可,不需要使用时,可以完成不用声明出来。如下

/**
* 后置通知,不需要参数时可以不提供
*/
@AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))")
public void AfterReturning(){
   System.out.println("我是后置通知...");
}
/**
* 后置通知
* returnVal,切点方法执行后的返回值
*/
@AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))",returning = "returnVal")
public void AfterReturning(JoinPoint joinPoint,Object returnVal){
   System.out.println("我是后置通知...returnVal+"+returnVal);
}
  • 异常通知@AfterThrowing

该通知只有在异常时才会被触发,并由throwing来声明一个接收异常信息的变量,同样异常通知也用于Joinpoint参数,需要时加上即可,如下:

/**
* 抛出通知
* @param e 抛出异常的信息
*/
@AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
public void afterThrowable(Throwable e){
  System.out.println("出现异常:msg="+e.getMessage());
}
  • 最终通知@After

该通知类似于finally代码块,只要应用了无论什么情况下都会执行。

/**
 * 无论什么情况下都会执行的方法
 * joinPoint 参数
 */
@After("execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))")
public void after(JoinPoint joinPoint) {
    System.out.println("最终通知....");
}
  • 环绕通知@Around

环绕通知既可以在目标方法前执行也可在目标方法之后执行,更重要的是环绕通知可以控制目标方法是否指向执行,第一个参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标函数,proceed()的返回值就是环绕通知的返回值。同样的,ProceedingJoinPoint对象也是可以获取目标对象的信息,如类名称,方法参数,方法名称等等。

@Around("execution(* com.zejian.spring.springAop.dao.UserDao.*User(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("我是环绕通知前....");
    //执行目标函数
    Object obj= (Object) joinPoint.proceed();
    System.out.println("我是环绕通知后....");
    return obj;
}

通知传参

在Spring AOP中,除了execution和bean指示符不能传递参数给通知方法,其他指示符都可以将匹配的方法相应参数或对象自动传递给通知方法。获取到匹配的方法参数后通过”argNames”属性指定参数名。如下,需要注意的是args(指示符)、argNames的参数名与before()方法中参数名 必须保持一致即param。

@Before(value="args(param)", argNames="param") //明确指定了    
public void before(int param) {    
    System.out.println("param:" + param);    
}

当然也可以直接使用args指示符不带argNames声明参数,如下:

@Before("execution(public * com.zejian..*.addUser(..)) && args(userId,..)")  
public void before(int userId) {  
    //调用addUser的方法时如果与addUser的参数匹配则会传递进来会传递进来
    System.out.println("userId:" + userId);  
}

args(userId,…)该表达式会保证只匹配那些至少接收一个参数而且传入的类型必须与userId一致的方法,记住传递的参数可以简单类型或者对象,而且只有参数和目标方法也匹配时才有会有值传递进来。

Aspect优先级

如果在不同的切面中定义多个通知响应同一个切点,进入时则优先级高的切面类中的通知函数优先执行,退出时则最后执行,如下定义AspectOne类和AspectTwo类并实现org.springframework.core.Ordered 接口,该接口用于控制切面类的优先级,同时重写getOrder方法,定制返回值,返回值(int 类型)越小优先级越大。

/**
 * Created by zejian on 2017/2/20.
 * Blog :  [原文地址,请尊重原创]
 */
@Aspect
public class AspectOne implements Ordered {

    /**
     * Pointcut定义切点函数
     */
    @Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.deleteUser(..))")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void beforeOne(){
        System.out.println("前置通知..AspectOne..执行顺序1");
    }

    @Before("myPointcut()")
    public void beforeTwo(){
        System.out.println("前置通知..AspectOne..执行顺序2");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningThree(){
        System.out.println("后置通知..AspectOne..执行顺序3");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningFour(){
        System.out.println("后置通知..AspectOne..执行顺序4");
    }

    /**
     * 定义优先级,值越低,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}


//切面类 AspectTwo.java
@Aspect
public class AspectTwo implements Ordered {

    /**
     * Pointcut定义切点函数
     */
    @Pointcut("execution(* com.zejian.spring.springAop.dao.UserDao.deleteUser(..))")
    private void myPointcut(){}

    @Before("myPointcut()")
    public void beforeOne(){
        System.out.println("前置通知....执行顺序1--AspectTwo");
    }

    @Before("myPointcut()")
    public void beforeTwo(){
        System.out.println("前置通知....执行顺序2--AspectTwo");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningThree(){
        System.out.println("后置通知....执行顺序3--AspectTwo");
    }

    @AfterReturning(value = "myPointcut()")
    public void AfterReturningFour(){
        System.out.println("后置通知....执行顺序4--AspectTwo");
    }

    /**
     * 定义优先级,值越低,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 2;
    }
}

4.2 基于XML配置实现

案例:

定义一个切面类

/**
 * Created by zejian on 2017/2/20.
 * Blog :  [原文地址,请尊重原创]
 */
public class MyAspectXML {

    public void before(){
        System.out.println("MyAspectXML====前置通知");
    }

    public void afterReturn(Object returnVal){
        System.out.println("后置通知-->返回值:"+returnVal);
    }

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("MyAspectXML=====环绕通知前");
        Object object= joinPoint.proceed();
        System.out.println("MyAspectXML=====环绕通知后");
        return object;
    }

    public void afterThrowing(Throwable throwable){
        System.out.println("MyAspectXML======异常通知:"+ throwable.getMessage());
    }

    public void after(){
        System.out.println("MyAspectXML=====最终通知..来了");
    }
}

通过配置文件的方式声明如下:

<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.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--<context:component-scan base-package=""-->

    <!-- 定义目标对象 -->
    <bean name="productDao" class="com.zejian.spring.springAop.dao.daoimp.ProductDaoImpl" />

    <!-- 定义切面 -->
    <bean name="myAspectXML" class="com.zejian.spring.springAop.AspectJ.MyAspectXML" />
    <!-- 配置AOP 切面 -->
    <aop:config>
        <!-- 定义切点函数 -->
        <aop:pointcut id="pointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.add(..))" />

        <!-- 定义其他切点函数 -->
        <aop:pointcut id="delPointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.delete(..))" />

        <!-- 定义通知 order 定义优先级,值越小优先级越大-->
        <aop:aspect ref="myAspectXML" order="0">
            <!-- 定义通知
            method 指定通知方法名,必须与MyAspectXML中的相同
            pointcut 指定切点函数
            -->
            <aop:before method="before" pointcut-ref="pointcut" />

            <!-- 后置通知  returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
            <aop:after-returning method="afterReturn" pointcut-ref="pointcut"  returning="returnVal" />

            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut"  />

            <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>

            <!--
                 method : 通知的方法(最终通知)
                 pointcut-ref : 通知应用到的切点方法
                -->
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

4.3 常用术语

1. 连接点(Join point)

可以理解为目标对象的所有方法

2. 切点(Pointcut)

通过对连接点进行过滤得到的目标方法

3. 通知(Advice)

对目标方法的增强操作

4. 切面(Aspect)

切点和通知共同组成切面

5. 织入(Weaving)

将通知代码应用到目标方法的过程