1.3(Spring学习笔记)Spring-AOP

一、AOP简介

  AOP面向切面编程,是将一种横向抽取机制,将多个类中需要使用的方法提取出来。

  例如,这里有两个类,一个Cat,一个Dog,动物都需要吃饭睡觉,如果按照传统的思想。

  给两类中都添加吃饭和睡觉的方法,如果有成百上千个类呢?是不是很麻烦而且容易出错。

  那么我们将这些方法(切面)提取出来,当Cat和Dog到达合适的时候(切入点)

  就将提取出来的方法插入到执行流程中。

  

  有没有感觉这个有点像代理模式,我们先用静态代理来实现这个想法。

  假设动物的一天是起床->饲养员准备吃的->动物叫(要吃饭了)->饲养员喂食物。

  按照传统的思路是每个动物都写上所有方法,这样很臃肿,我们采用AOP的思想。

  首先所有动物都要吃东西,所以饲养员准备吃的这个方法和喂食这个方法可以提取出来。

  

  Animal接口

public interface Animal {
    public void eat();
    
}

  Cat.java

public class Cat implements Animal{

    @Override
    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("喵喵喵");
    }
}

  Dog.java

public class Dog implements Animal{

    @Override
    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("汪汪汪");
    }
}

  Proxy.java(代理类)

public class Proxy implements Animal{
    private Animal ani;
    
    public Proxy(Animal ani) {
        this.ani = ani;
    }
    
    @Override
    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("准备饲料");//切面
        ani.eat();                //执行流程
        System.out.println("喂食");//切面
    }
}

  测试:  

public class Main {
    public static void main(String[] args) {
        Proxy proxy = new Proxy(new Cat());
        proxy.eat();
    }
}
运行结果:
准备饲料 喵喵喵 喂食

可以看到,面向切面将重复的部分提取出去了,到了合适的时机(例如猫在喵喵后才喂食)在将方法切入进流程中。

传统的思想就相当于一个动物配一个饲养员,面向切面就相当于所有动物共用一个饲养员。

二、动态代理

静态代理可以实现这种思想,但是一旦需求多了,后期需要添加很多代码,对规模较大的情况不适用。

这时就需要使用动态代理。想像一下每次进入某个地方需要刷卡,然后进入,同时后台还要进行记录。

我们就可以把检查和记录提出出来。

   2.1基于JDK的动态代理

  使用动态代理我们首先来创建接口

  Person.java

public interface Person {
        public void intoLib(); //去图书馆
        public void intoTeachingBuilding(); //去教学楼
}

  

  Person的实现类Studetn.java

public class Student implements Person{
    public void intoLib(){
        System.out.println("进入图书馆");
    }
    
    public void intoTeachingBuilding() {
        System.out.println("进入教学楼");
    }
}

  我们在来编写切面

  Check.java

public class Check {
    public void enter() {
        System.out.println("进门检查");
    }
    
    public void log() {
        System.out.println("记录");
    }
}

  动态代理类 DP.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DP implements InvocationHandler{
    
    private Person per;
    public Object bind(Person per) {//将代理对象绑定
        this.per = per;
        //返回代理后的对象              获取类加载器             被代理对象所有接口              自身
        return Proxy.newProxyInstance(DP.class.getClassLoader(), per.getClass().getInterfaces(), this);
    }
    
    //返回的代理后的对象,每次调用方法都由invoke处理
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        // 切面
        Check check = new Check();
        check.enter();//进门检查 
        Object obj = method.invoke(per, args);//执行被代理对象的方法
        check.log();//记录
        return obj;
    }
}

  测试:

public class Main {
    public static void main(String[] args) {
          DP dp  =  new  DP() ;
          Person stu = (Person) dp.bind(new Student());
          stu.intoLib();
          stu.intoTeachingBuilding();
    }
}
运行结果:
进门检查 进入图书馆 记录 进门检查 进入教学楼 记录

  2.2基于CGLIB的动态代理

  JDK固然可以实现动态代理,但还有有其局限性,比如被代理需要实现一些接口。

  在对没有实现接口的类进行动态代理可以使用CGLIB。

  CGLIB(Cod Generation Library)是一款高性能开源的代码包,采用字节码技术对代理对象生成一个子类并对其增强来实现。 

  CGLIB意见集成到Spring核心包中。(AOP包不要忘记导入)

  

  首先创建被代理类Studetn.java

public class Student{
    public void intoLib(){
        System.out.println("进入图书馆");
    }
    
    public void intoTeachingBuilding() {
        System.out.println("进入教学楼");
    }
}

  

  接着创建切面Check.java

public class Check {
    public void enter() {
        System.out.println("进门检查");
    }
    
    public void log() {
        System.out.println("记录");
    }
}

  接着通过CGLIB创建代理对象

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.srping.aop.dp.Check;

public class CGLIB implements MethodInterceptor{
    public Object bind(Object obj) {
        //创建增强对象
        Enhancer enhancer = new Enhancer();
        //设代理对象为父类,用于生成子类并对子类增强
        enhancer.setSuperclass(obj.getClass());
        //设置回调
        enhancer.setCallback(this);
        //返回创建的代理类
        return enhancer.create();
    }
    
    //类似动态代理的 invoke 
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        //切面
        Check check = new Check();
        check.enter();//进门检查 
        Object obj = methodProxy.invokeSuper(proxy, args);//执行被代理对象的方法
        check.log();//记录
        return obj;
    }
}

  最后测试

public class Main {
    public static void main(String[] args) {
          CGLIB cglib = new CGLIB();
          Student stu = (Student)cglib.bind(new Student());
          stu.intoLib();
          stu.intoTeachingBuilding();
    }
}
运行结果:
进门检查 进入图书馆 记录 进门检查 进入教学楼 记录

三、 基于Spring代理类实现AOP

  Spring中有一个ProxyFactoryBena可以实现代理,

  Spring根据切入分类:

  org. aopalliance.intercept.MethodInterceptor (环绕通知)

    org.springframework.aop.MethodBeforeAdvice (前置通知)

    org.springframework.aop.AfterReturningAdvice (后置通知)

    org .springframework.aop.ThrowsAdvice (异常通知)

  ProxyFactoryBean属性

  target:被代理的目标。

  proxyTargetClass:设置是否对类进行代理。

  proxyInterfaces:代理实现的接口,可以是多个(多个时采用<list><vaue>...</list>)

  (如果设置的是对类进行代理,则该属性可以不设置)

  interceptorNames:设置需要织入的advice。

  singleton:返会的代理类是否为单例。

  optimize:设置为true时,强制使用CGLIB。

  我们来看一个例子

  创建接口:

public interface Person {
        public void intoLib(); //去图书馆
        public void intoTeachingBuilding(); //去教学楼
}

  实现接口的类Studetn.java

public class Student implements Person{
    public void intoLib(){
        System.out.println("进入图书馆");
    }
    
    public void intoTeachingBuilding() {
        System.out.println("进入教学楼");
    }
}

  切面设置环绕通知

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;


//环绕通知
public class Check implements MethodInterceptor{
    public void enter() {
        System.out.println("进门检查");
    }
    
    public void log() {
        System.out.println("记录");
    }

    @Override
    public Object invoke(MethodInvocation me) throws Throwable {
        // TODO Auto-generated method stub
        enter();//进门检查
        Object obj = me.proceed();//执行被代理对象自身被调用方法
        log();//记录
        return obj;
    }    
}

  beans.xml配置 

<?xml  version="1.0"  encoding="UTF-8"?> 
<beans  xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context"         
        xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context 
             http://www.springframework.org/schema/context/spring-context-4.3.xsd" > 
    
    
    <!-- 开启注解 -->
    <!--  <context:annotation-config/>  -->
    <!-- 配置Bean的实例 -->
    
    <!-- 1.目标类,即被代理的类 -->
    <bean id = "student" class = "com.spring.aop.proxyFactoryBean.Student">
    </bean>
    <!-- 2.通知(切面) -->
    <bean id = "check" class = "com.spring.aop.proxyFactoryBean.Check">
    </bean>
    <!-- 3.使用ProxyFactoryBena定义一个代理类 -->
    <bean id = "proxy" class = "org.springframework.aop.framework.ProxyFactoryBean">
        
        <!-- 3.1指定代理方法  false为不进行类代理,代理的是接口。true反之-->
        <property name="proxyTargetClass" value = "false"></property>
        <!-- 3.2 指定代理实现的接口  如果是proxyTargetClass = true 则该属性可不设置-->
        <property name="proxyInterfaces" value="com.spring.aop.proxyFactoryBean.Person"></property>
        <!-- 3.3指定目标对象,即被代理类  -->
        <property name = "target" ref = "student"></property>
        <!-- 3.4指定通知adivce -->
        <property name="interceptorNames" value = "check"></property>
        
    </bean>
</beans>

  

  测试:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
         ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
         Person stu = (Person)ac.getBean("proxy"); //由于本例采用代理接口,所以只能强转为Person接口。
         stu.intoLib();                //如果想强转为Student,需要采用类代理,proxyTargetClass设置为true
         stu.intoTeachingBuilding();
    }
}
运行结果:
二月 11, 2019 8:43:56 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@25f38edc: startup date [Mon Feb 11 20:43:56 CST 2019]; root of context hierarchy 二月 11, 2019 8:43:56 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [beans.xml] 进门检查 进入图书馆 记录 进门检查 进入教学楼 记录

  如果采用类代理,则可以将接口全部去掉。

  然后可以将xml文件中的“proxyTargetClass”改为trey、将“proxyInterfaces”去掉。

<!-- 1.目标类,即被代理的类 -->
    <bean id = "student" class = "com.spring.aop.proxyFactoryBean.Student">
    </bean>
    <!-- 2.通知(切面) -->
    <bean id = "check" class = "com.spring.aop.proxyFactoryBean.Check">
    </bean>
    <!-- 3.使用ProxyFactoryBena定义一个代理类 -->
    <bean id = "proxy" class = "org.springframework.aop.framework.ProxyFactoryBean">
        
        <!-- 3.1指定代理方法  ture为进行类代理,不进行接口代理。false反之-->
        <property name="proxyTargetClass" value = "true"></property>
        
        <!-- 3.3指定目标对象,即被代理类  -->
        <property name = "target" ref = "student"></property>
        <!-- 3.4指定通知adivce -->
        <property name="interceptorNames" value = "check"></property>
        
    </bean>

  

  测试: 

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
         ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
         //Person stu_ = (Person)ac.getBean("proxy"); //采用类代理是可以强转为接口,也可以强转为类。
         Student stu = (Student)ac.getBean("proxy");
         stu.intoLib();                
         stu.intoTeachingBuilding();
    }
}
运行结果:
与上例相同。

  

四、AspectJ

  AspectJ是一个基于Java语言的AOP框架。

  使用AspectJ之前需要先导入两个包:

  -Spring-aspects-x.x.x.RELEASE.jar

  -aspectjweaver-1.8.10.jar

   下载地址:https://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.10

  4.1AspectJ基于xml实现AOP

  先来看下xml中关于AOP的一些元素。

  <aop:config>:用于定义切面、切入点、通知等。

    <aop:aspect>:<aop:config>的子元素,用于定义一个切面。

    id:该切面的唯一标识。

    ref:该切面所对应的切面类。

    <aop:pointcut>:<aop:config><aop:aspect>的子元素,用于定义一个切入点。

    如果切入点配置在切面中,则只对当前切面有效,如果定义在<aop:config>中则对所有切面有效。

    id:该切入点的唯一标识。

    execution:切入点表达式。

    <aop:before>:配置前置通知。

    <aop:after-returning>:配置后置通知。

    <aop:around>:配置环绕通知。

    <aop:after-throwing>:配置异常通知。

    <aop:after>:配置最终通知。

    poincut:设置一个切入点标识。

    pointcut-ref:指定一个切入点表达式。

    method:指定当前通知执行的方法。

    throwing:指定一个形参名,用于获取抛出的异常,仅对after-thorwing有效。

    returning:指定一个形参名,用于获取目标方法的返回值,仅对after-returning有效。

下面来看一个例子

/**  
* 创建时间:2019年2月16日 下午8:17:43  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:UserDao.java  
* 类说明: UserDao接口
*/

public interface UserDao {
    public void add();
    public void delete();
    public int test();
}
import org.springframework.stereotype.Repository;

/**  
* 创建时间:2019年2月16日 下午8:19:01  
* 项目名称:SpringAOP  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:UserDaoImple.java  
* 说       明:UserDao接口的实现类   
*/
@Repository("userDao")
public class UserDaoImple implements UserDao{

    @Override
    public void add() {
        // TODO Auto-generated method stub
        System.out.println("添加用户");
    }

    @Override
    public void delete() {
        // TODO Auto-generated method stub
        System.out.println("删除用户");
    }
    
    public int test() {
        //int i = 1/0;
        System.out.println("test");
        return 10;
    }
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//切面类
/**  
* 创建时间:2019年2月16日 下午8:23:50  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:MyAspect.java  
* 说       明:切面类,定义了各种通知。 
*/
public class MyAspect {
    //前置通知
    public void myBefore(JoinPoint jp) {
        System.out.println("前置通知------");
        System.out.println("-目标类是:" + jp.getTarget());
        System.out.println("-目标方法是:" + jp.getSignature().getName());
    }
    
    //后置通知                      returnVal为xml中指定的形参名
    public void myAfter(JoinPoint jp,Object returnVal) {
        System.out.println("后置通知------");
        System.out.println("-目标方法是:" + jp.getSignature().getName());
        System.out.print("-目标方法返回值" + returnVal.toString());
    }
    
    //环绕通知
    public Object myAround(ProceedingJoinPoint pj) throws Throwable{
        System.out.println("环绕开始------");
        //执行对象本身调用的方法
        Object object = pj.proceed();
        System.out.println("环绕结束------");
        return object;
    }
    
    //异常通知                          e为xml中指定的形参名
    public void myThrowing(JoinPoint jp, Throwable e) {
        System.out.print("异常通知:" + e.getMessage());
    }
    
    //最终通知
    public void myFinally() {
        System.out.println("最终通知");
    }
}

beans.xml

<?xml  version="1.0"  encoding="UTF-8"?> 
<beans  xmlns="http://www.springframework.org/schema/beans" 
        xmlns:context="http://www.springframework.org/schema/context"   
        xmlns:aop="http://www.springframework.org/schema/aop"        
        xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/aop 
             http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > 
    <!-- 1.目标类 -->
    <bean id = "userDao" class = "com.spring.AspectJ.xml.UserDaoImple"> </bean>
    <!-- 2.切面类 -->
    <bean id = "myAspect" class = "com.spring.AspectJ.xml.MyAspect"></bean>
    <aop:config>
        <!-- 配置切面 -->
        <aop:aspect ref = "myAspect">
            <!-- 配置切入点 并为其指定一个名词 -->
            <aop:pointcut expression = "execution(* com.spring.AspectJ.xml.*.*(..))" id = "myPointCut"/>
            
            <!-- 设置通知和通知的切入点 -->
            <!-- 前置通知 指定前置通知执行的方法,并设置切入点 -->
            <aop:before method = "myBefore" pointcut-ref = "myPointCut"/>
            <!-- 后置通知 指定后置通知执行的方法,并设置切入点 returning指定后置通知方法的形参名,用于获取目标方法的返回值 -->
            <aop:after-returning method = "myAfter" pointcut-ref = "myPointCut" returning = "returnVal"/>
            <!-- 环绕通知 指定环绕通知执行的方法,并设置切入点-->
            <aop:around method="myAround" pointcut-ref = "myPointCut"/>
            <!-- 异常通知,如果程序没有发生异常则不会被执行  throwing指定异常通知方法的形参名 -->
            <aop:after-throwing method = "myThrowing" pointcut-ref = "myPointCut" throwing = "e" />
            <!-- 最终通知 ,无论如何都会执行-->
            <aop:after method = "myFinally" pointcut-ref = "myPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>

测试:

  

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**  
* 创建时间:2019年2月16日 下午8:50:39  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:Main.java  
* 说       明:  测试AspectJ基于XML实现AOP
*/
public class Main {
    public static void main(String[] args) {
        String beansXmlPathString = "com/spring/AspectJ/xml/beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(beansXmlPathString);
        UserDao userao = (UserDao)ac.getBean("userDao");
        userao.test();
    }
}
运行结果:
二月 18, 2019 10:36:14 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 10:36:14 CST 2019]; root of context hierarchy
二月 18, 2019 10:36:14 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/xml/beans.xml]
前置通知------
-目标类是:com.spring.AspectJ.xml.UserDaoImple@341b80b2
-目标方法是:test
环绕开始------
test
最终通知
环绕结束------
后置通知------
-目标方法是:test
-目标方法返回值10

我们将 UserDaoImple中test方法中的//int i = 1/0;的注释去掉,然后再运行看下结果:

二月 18, 2019 10:41:25 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 10:41:25 CST 2019]; root of context hierarchy
二月 18, 2019 10:41:25 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/xml/beans.xml]
前置通知------
-目标类是:com.spring.AspectJ.xml.UserDaoImple@341b80b2
-目标方法是:test
环绕开始------
最终通知
异常通知:/ by zeroException in thread "main" java.lang.ArithmeticException: / by zero
    at com.spring.AspectJ.xml.UserDaoImple.test(UserDaoImple.java:30)

由上面两个运行结果可以看出,环绕的是目标方法,前置通知最先执行,后置通知最后执行。

最终通知无论如何都会执行。

  4.2AspectJ基于annotation实现AOP

    基于注解实现AOP会比较方便,首先我们来了解几个注解。  

    @Aspect:定义一个切面类。

    @Pointcut:定义一个切入点,value指定切入点表达式,还需定义一个空方法为该切入点的名称。

    @Before:定义前置通知,value属性用于指定切入点。

    @AfterRunning:定义后置通知,value属性指定切入点,returninig属性指定形参名,用于访问方法返回值。

    @Around:定义环绕通知。value属性指定切入点。

    @AfterThrowing:定义异常通知,valuet同上,Throwing指定一个形参名,用于访问抛出的异常。

    @After:定义最终通知,value同上。

下面还是看一个例子理解注解实现AOP

/**  
* 创建时间:2019年2月16日 下午8:17:43  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:UserDao.java  
* 类说明: UserDao接口,注解实现
*/

public interface UserDao {
    public void add();
    public void delete();
    public int test();
}
import org.springframework.stereotype.Repository;

/**  
* 创建时间:2019年2月16日 下午8:19:01  
* 项目名称:SpringAOP  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:UserDaoImple.java  
* 说       明:UserDao接口的实现类,注解实现AOP   
*/
@Repository("userDao")//基于注解实例化Bena
public class UserDaoImple implements UserDao{

    @Override
    public void add() {
        // TODO Auto-generated method stub
        System.out.println("添加用户");
    }

    @Override
    public void delete() {
        // TODO Auto-generated method stub
        System.out.println("删除用户");
    }
    
    public int test() {
        //int i = 1/0;
        System.out.println("test");
        return 10;
    }
}
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.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//切面类

/**  
* 创建时间:2019年2月17日 下午8:24:15  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:MyAspect.java  
* 说       明: AspectJ注解实现AOP。 
*/

//基于注解定义切面,同时定义当前类
@Aspect
@Component
public class MyAspect {
    //定义一个切入点表达式,并设置一个空方法为其命名。
    @Pointcut("execution(* com.spring.AspectJ.annotation.*.*(..))")
    private void myPointCut() {};//定义一个void空方法为切入点命名,类似 id = "myPointCut"
    
    //前置通知,指定切入点
    @Before("myPointCut()")
    public void myBefore(JoinPoint jp) {
        System.out.println("前置通知------");
        System.out.println("-目标类是:" + jp.getTarget());
        System.out.println("-目标方法是:" + jp.getSignature().getName());
    }
    
    //后置通知                               returnVal为指定的形参名
    @AfterReturning(value = "myPointCut()" , returning = "returnVal")
    public void myAfter(JoinPoint jp,Object returnVal) {
        System.out.println("后置通知------");
        System.out.println("-目标方法是:" + jp.getSignature().getName());
        System.out.print("-目标方法返回值" + returnVal.toString());
    }
    
    //环绕通知
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint pj) throws Throwable{
        System.out.println("环绕开始------");
        //执行对象本身方法
        Object object = pj.proceed();
        System.out.println("环绕结束------");
        return object;
    }
    
    //异常通知                                e为指定的形参名
    @AfterThrowing(value = "myPointCut()", throwing = "e")
    public void myThrowing(JoinPoint jp, Throwable e) {
        System.out.print("异常通知:" + e.getMessage());
    }
    
    //最终通知
    @After("myPointCut()")
    public void myFinally() {
        System.out.println("最终通知");
    }
}

beans.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.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    
    <!-- 扫描指定包下@Component @Controller@Service等这些注解,将其对应类注册 -->
     <context:component-scan base-package="com.spring.AspectJ.annotation"></context:component-scan>
    <!-- 开启注解 -->
    <aop:aspectj-autoproxy/>  
</beans>

如果UserDaoImple和MyAspect不使用注解注册,则 context:component-scan可以去掉。

但需要手动为其配置bean:

<bean id = "userDao" class = "com.spring.AspectJ.annotation.UserDaoImple"/>
<bean id = "myAspect" class = "com.spring.AspectJ.annotation.MyAspect"/>

切面类首先是一个类,其次它是一个切面类,只要是类通过Spring实例化就要为其注册。

测试:

  

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

/**  
* 创建时间:2019年2月16日 下午8:50:39  
* 项目名称:Spring  
* @author hcf  
* @version 1.0   
* @since JDK 1.8.0_201  
* 文件名称:Main.java  
* 说       明:  测试AspectJ基于Annotation实现AOP
*/
public class Main {
    public static void main(String[] args) {
        String beansXmlPathString = "com/spring/AspectJ/annotation/beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(beansXmlPathString);
        com.spring.AspectJ.annotation.UserDao userDao = (com.spring.AspectJ.annotation.UserDao)ac.getBean("userDao");
        userDao.test();
    }
}
运行结果:
二月 18, 2019 11:16:48 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@300ffa5d: startup date [Mon Feb 18 11:16:48 CST 2019]; root of context hierarchy
二月 18, 2019 11:16:48 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/spring/AspectJ/annotation/beans.xml]
环绕开始------
前置通知------
-目标类是:com.spring.AspectJ.annotation.UserDaoImple@17c1bced
-目标方法是:test
test
环绕结束------
最终通知
后置通知------
-目标方法是:test
-目标方法返回值10

猜你喜欢

转载自www.cnblogs.com/huang-changfan/p/10361955.html