zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Python列表List增删改查、拷贝
    面向对象之继承——python篇
    面向对象——python篇
    异常——python基础篇
    函数进阶
    文件操作——python基础篇
    函数(一)——python基础篇
    字典——python基础篇
    特有的字符串格式化函数format——python篇
    webdriver对应谷歌版本下载地址
  • 原文地址:https://www.cnblogs.com/huang-changfan/p/10361955.html
Copyright © 2011-2022 走看看