zoukankan      html  css  js  c++  java
  • spring(三):springAOP的各种织入方式一览

    前言:

    上一篇简单使用反射和jdk的动态代理模拟了AOP工作原理。在这里将讲阐述AOP如何把代码织入到目标对象的方法上。


    一:这里介绍一下AOP一些名词概念

    (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知。

    (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。这就是目标对象的方法。

    (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,
        afterThrowing,around。这些就是需要织入到连接点中的代码。

    (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

    (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,
        也可以是CGLIB代理,前者基于接口,后者基于子类

    (6)advisor:增强器,用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).

      (7)wave:织入,把切面/切面类和目标类的动态接入。           

                             

    二:Advice(通知)

    AOP的通知有before前置通知。after后置通知,无论是否成功返回都会执行。afterReturning后置通知,只有成功返回后才会执行。

    afterThrowing抛出异常通知,顾名思义,只有抛出异常的时候才会执行。around环绕通知,简单来说就是集合了before和after的

    功能。

    这里基于xml文件实现把切面类和目标类动态接入。

    (1)pojo类:Account.java

    package com.cnblogs.aop.pojo;
    /**
     * 简单模拟银行账户
     *
     */
    public class Account {
        // 卡号
        private Long id;
        // 姓名    
        private String name;
        //余额
        private double balance;
        
        public Account() {
            super();
            // TODO Auto-generated constructor stub
        }
        
        public Account(Long id, String name, double balance) {
            super();
            this.id = id;
            this.name = name;
            this.balance = balance;
        }
    
       // get和set方法
    }

     

    (2)目标类:这里使用的是JDK的接口代理模式,所以需要准备一个接口,和接口的实现类,即目标类。

    package com.cnblogs.aop.service;
    
    public interface BankService {
        /**
         * 存钱
         * @param money
         */
        public Boolean save(double money) throws Exception;
        
        /**
         * 取钱
         * @param money
         * @return
         */
        public boolean withdraw(double money) throws Exception;
    }

     

    package com.cnblogs.aop.service.impl;
    
    import com.cnblogs.aop.pojo.Account;
    import com.cnblogs.aop.service.BankService;
    
    public class BankServiceImpl implements BankService {
        
        private Account account;
        
        public Account getAccount() {
            return account;
        }
    
        public void setAccount(Account account) {
            this.account = account;
        }
    
        @Override
        public Boolean save(double money) throws Exception {
            System.out.println("存钱: " + money);
            account.setBalance(account.getBalance() + money);
            return true;
        }
    
        @Override
        public boolean withdraw(double money) throws Exception{
            System.out.println("取钱: " + money);
            if(account.getBalance() < money) {
                throw new Exception("余额不足");    
            }
            account.setBalance(account.getBalance() - money);
            return true;
        }
    
    }

     

    (3):目标类准备好以后,就要准备切面类了。这个切面类直接集合了所有的通知类型。

    package com.cnblogs.aop.aspect;
    
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.aop.ThrowsAdvice;
    
    /**
     * 切面类
     * 实现了前置通知 MethodBeforeAdvice
     * 成功返回才执行的后置通知 
     * 环绕通知
     * 抛出异常才会执行的通知
     * 
     *
     */
    public class MyLogger implements  MethodBeforeAdvice,AfterReturningAdvice,
            MethodInterceptor,ThrowsAdvice{
        
    
        /**
         * 前置通知
         * @param method 连接点
         * @param args 连接点参数
         * @param target 目标对象
         */
        @Override
        public void before(Method mehtod, Object[] args, Object target) throws Throwable {
            // 添加日志功能
            System.out.println("前置通知: " + new SimpleDateFormat().format(new Date()));
        }
        
        /**
         * 后置通知会接收到连接点的返回值,连接点返回值必须为引用类型,否则
         * 报错:org.springframework.aop.AopInvocationException: 
         * Null return value from advice does not match primitive return type for
         * @param returnValue 连接点的返回值【如果有的话】
         * @param method 连接点
         * @param args 连接点方法参数
         * @param target 目标对象【委托对象】
         */
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            // 添加日志功能
            System.out.println("成功返回才执行的后置通知: " + new SimpleDateFormat().format(new Date()));    
        }
        
        /**
         * 环绕通知
         * 在调用连接点前为前置通知
         * 在调用连接点后为后置通知
         */
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("环绕通知之前置: " + 
                        new SimpleDateFormat().format(new Date()));    
            // 调用连接点
            invocation.proceed();
            
            System.out.println("环绕通知之后置: " + 
                    new SimpleDateFormat().format(new Date()));    
            return null;
        }
        
        /**
         * 抛出异常才会执行的通知比较特殊,它的接口类体为空,
         * 只起一个标记作用。
         * 不过会默认调用afterThrowing方法作为通知。
         * 该方法重载,有4个参数的和1个参数的
         * 当两个重载方法都存在时优先调用4个参数的
         */
        public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
            System.out.println("抛出异常才会执行的通知(4参): " + 
                    e.toString());    
        }
    
        public void afterThrowing(Exception e) {
            System.out.println("抛出异常才会执行的通知(1参): " + 
                    e.toString());    
        }
    
    }

     

    (4):织入

    advice.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:context="http://www.springframework.org/schema/context"
        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">
        
        <!-- 银行账号 -->
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
        </bean>
        
        <!-- 生成代理对象
              这里使用的是spring的一个代理对象工厂类产生的
          -->
        <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!-- 注入目标对象 -->
            <property name="target" ref="service"></property>
    
            <!-- 注入目标对象所实现的接口 可以有多个接口,基于JDK的接口代理 -->
            <property name="proxyInterfaces">
                <list>
                    <value>com.cnblogs.aop.service.BankService</value>
                </list>
            </property>
    
            <!-- 注入advice -->
            <property name="interceptorNames">
                <list>
                    <value>log</value>
                </list>
            </property>
    
    </bean>
    </beans>

     

    (5):测试  

    package com.cnblogs.aop.jtest;

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

    import com.cnblogs.aop.service.BankService;

    @SuppressWarnings("resource")
    public class AOPTest {
        
        @Test
        public void advice(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/aspect/advice.xml";
                
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                // 从容器中获取代理对象
                BankService proxy = (BankService) container.getBean("proxy");
                // 执行目标对象中的方法
    //            proxy.save(1000.0);
                
                // 测试通知会对那些方法起作用
    //            proxy.getClass(); // 无
    //            proxy.toString(); // 有
                
                // 测试抛出异常通知
    //            proxy.withdraw(5000.0);
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    1.环绕通知的前置优先前置通知输出,后置通知优先环绕通知的后置输出。

    环绕通知之前置: 19-5-28 下午7:29
    前置通知: 19-5-28 下午7:29
    存钱: 1000.0
    成功返回才执行的后置通知: 19-5-28 下午7:29
    环绕通知之后置: 19-5-28 下午7:29

    2.通知对那些方法起作用?

      单独调用getClass()方法时没有通知输出,toString()方法通知有输出。去java.lang.Objcet查找两个方法,

    public final Class<?> getClass(),public String toString().简单对比发现只要是非final类型修饰的,通过代理对象调用

    目标对象方法时都会把通知织入。

    有时候我们希望目标对象的某些方法被织入,其它方法不想被织入,当时又不想用final修饰那些不想被织入的方法该

    怎么办?增强器可以起到这个效果,筛选需要的方法织入。

     

    三:Advisor增强器

    增强器可以筛选目标类中那些方法被织入。

     advisor.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:context="http://www.springframework.org/schema/context"
        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">
        
        <!-- 银行账号 -->
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
        </bean>
        
        <!-- 配置增强器的bean对象 -->
        <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <!-- 注入advice -->
            <property name="advice" ref="advice"/>
            <!-- 注入需要被拦截的目标对象中的方法(连接点) -->
            <property name="patterns">
                <list>
                    <value>.*save</value>
                    <value>.*withdraw</value>
                </list>
            </property>
        </bean>
        
        <!-- 生成代理对象
              这里使用的是spring的一个代理对象工厂类产生的
          -->
        <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!-- 注入目标对象 -->
            <property name="target" ref="service"></property>
    
            <!-- 注入目标对象所实现的接口 可以有多个接口,基于JDK的接口代理 -->
            <property name="proxyInterfaces">
                <list>
                    <value>com.cnblogs.aop.service.BankService</value>
                </list>
            </property>
    
            <!-- 注入advice -->
            <property name="interceptorNames">
                <list>
                    <value>advisor</value>
                </list>
            </property>
      </bean>
    </beans>

    补充:与advice.xml文件对比,发现只是多了一个org.springframework.aop.support.RegexpMethodPointcutAdvisor类的bean对象,该类就是增强器,

    里面需要注入切面类的bean对象,以及指定那些方法被织入。

    其次在生成代理对象的bean标签中,注入advice是注入增强器。

     

    测试:

    @Test
        public void advisor(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/advisor/advisor.xml";
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                // 从容器中获取代理对象
                BankService proxy = (BankService) container.getBean("proxy");
                
    //            proxy.toString();
    //            proxy.save(1000);
                
                proxy.getClass();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

     

     四:AutoProxy,自动代理

     如果目标类不止一个,为多个目标类生成代理对象需要配置多个

    <bean name="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">...... </bean>,

    自动代理可以用很少的xml配置给目标对象生成代理对象。自动代理用到增强器的功能。

    autoProxy.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:context="http://www.springframework.org/schema/context"
        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">
        
        <!-- 银行账号 -->
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
        </bean>
        
        <!-- 配置增强器的bean对象 -->
        <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <!-- 注入advice -->
            <property name="advice" ref="log"/>
            <!-- 注入需要被拦截的目标对象中的方法(连接点) -->
            <property name="patterns">
                <list>
                    <value>.*save</value>
                    <value>.*withdraw</value>
                </list>
            </property>
        </bean>
        
        <!-- 配置自动代理对象 -->
        <bean name="proxy" 
            class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        </bean>
    </beans>

     

    1.不能通过proxy拿代理对象,通过目标对象的名字拿代理对象。

    2.当前xml文件中一定要有一个增强器advisor,配置自动代理对象不需要注入任何东西。

    3.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象(CGLib的方式)。

    4.如果目标对象有增强器中筛选的方法,将来调用方法的时候会动态织入。

     

    测试:

    @Test
        public void autoProxy(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/autoProxy/autoProxy.xml";
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                BankService target = (BankService) container.getBean("service");
                target.save(1000.0);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

     结果:

    环绕通知之前置: 19-5-29 下午4:22
    前置通知: 19-5-29 下午4:22
    存钱: 1000.0
    成功返回才执行的后置通知: 19-5-29 下午4:22
    环绕通知之后置: 19-5-29 下午4:22

     

    2.AutoProxyByName 通过名字进行自动代理

    虽然自动代理可以很方便的给xml文件中的目标对象设置对应的代理对象,但是并不是xml文件中的所有对象都是我们的目标对象,

    我们更想希望可以进一步筛选出某几个对象为我们的目标对象。

    autoProxyByName.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:context="http://www.springframework.org/schema/context"
        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">
        
        <!-- 银行账号 -->
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
        </bean>
        
        <!-- 配置增强器的bean对象 -->
        <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <!-- 注入advice -->
            <property name="advice" ref="log"/>
            <!-- 注入需要被拦截的目标对象中的方法(连接点) -->
            <property name="patterns">
                <list>
                    <value>.*save</value>
                    <value>.*withdraw</value>
                </list>
            </property>
        </bean>
        
        <!-- 配置代理对象 -->
        <!-- 这里使用自动代理的方式 autoproxybyname -->
        <bean name="proxy" 
                class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
            <!-- 注入需要被代理的对象名字 -->
               <property name="beanNames">
                   <list>
                    <!-- value中填写bean标签的id或name属性值 -->
                       <value>service</value>
                   </list>
               </property>
               <!-- 注入advice或者advisor -->
               <property name="interceptorNames">
                   <list>
                       <value>advisor</value>
                   </list>
               </property>
         </bean>
    </beans> 

    使用byName自动代理的时候需要注意的方面:

     

                 1.当前的配置里面"有没有"advisor的配置"都没关系"

     

                 2.不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象.

     

                 3.从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。

    五:使用<aop>标签完成织入功能

     execution(modifiers-pattern ret-type-pattern declaring-type-pattern name-pattern(param-pattern)   throws-pattern)

    除了返回类型模式(ret-type-pattern),名字模式(name-pattern),参数模式(param-pattern)是必须的,

    其它模式都是可选的

    ret-type-pattern: ' * '代表匹配任意返回类型,java.lang.String匹配String返回类型。

    name-pattern: 名字模式匹配的是方法名。 你可以使用 * 通配符作为所有或者部分命名模式。

    param-pattern:参数匹配, ' () '匹配无参,' (*) '匹配一个参数,‘(..)’匹配任意参数(0或多个),

    ' (*,String) ' 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个必须是String类型。

    下面给出一些常见切入点表达式的例子。 

        1)任意包下的任意类中的公共方法的执行: 

                      execution(public * *(..))

        2)任何一个以“set”开始的方法的执行: 

                      execution(* set*(..))

        3)BankService接口的任意方法的执行:   

                      execution(* com.cnblogs.service.BankService.*(..))

        4)定义在service包里的任意方法的执行: 

                      execution(* com.cnblogs.service.*.*(..))

        5)定义在service包或者子包里的任意方法的执行: 

                      execution(* com.cnblogs.service..*.*(..))

     

    1.第一种方式使用<aop>标签

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        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
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
        
        <!-- 银行账号 -->
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aspect.MyLogger">
        </bean>
        
        <!-- 配置aop的代理  也启用了自动代理的功能-->
        <aop:config>
            <!-- 定义一个切入点 -->
            <aop:pointcut 
                expression="execution(public * com.cnblogs.aop.service.BankService.save(*))" 
                id="save"/>
            <aop:pointcut 
                expression="execution(public * com.cnblogs.aop.service.BankService.withdraw(*))" 
                id="withdraw"/>
    
            <!-- 定义哪一个advice在哪一个切入点上面起作用 -->
            <aop:advisor advice-ref="log" pointcut-ref="save"  />
            <aop:advisor advice-ref="log" pointcut-ref="withdraw"  />
        </aop:config>
    
    </beans>

    注意:

                      1.从spring容器中拿代理对象的时候也是要用目标对象的名字来拿。

                       2.没有实现任何接口的目标对象也能产生代理对象。

     

    测试:

    @Test
        public void aop1(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/aopConfig/config1.xml";
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                BankService target = (BankService) container.getBean("service");
                target.save(1000.0);
                target.withdraw(20000.0);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    环绕通知之前置: 19-5-29 下午7:06
    前置通知: 19-5-29 下午7:06
    存钱: 1000.0
    成功返回才执行的后置通知: 19-5-29 下午7:06
    环绕通知之后置: 19-5-29 下午7:06
    环绕通知之前置: 19-5-29 下午7:06
    前置通知: 19-5-29 下午7:06
    取钱: 20000.0
    抛出异常才会执行的通知(1参): java.lang.Exception: 余额不足

     

    2.第二种方式使用aop

    在一个切面类中定个多个方法,根据xml文件的配置每个方法都可以织入到切入点的不同位置,并且advice是在aop的标签中进行配置,

    不需要再写对应的advice类了。

    切面类:

    package com.cnblogs.aop.aopConfig;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    /**
     * 切面类,这里包含了多个切面方法,可以把每个切面方法织入到不同切入点的不同位置
     * 
     */
    public class XmlHandler {
        public void beforeTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" before...");
        }
        
        
        public void afterTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" after...");
        }
        
        public void afterReturningTest(JoinPoint p){
            System.out.println(p.getSignature().getName()+" afterReturning");
        }
        
        //在和aroundAdvice结合的时候,这个方法一定要加上这个ProceedingJoinPoint类型的参数
        public Object aroundTest(ProceedingJoinPoint pjp)throws Throwable{
            //JoinPoint对象不能调用连接点所表示的方法 
            //ProceedingJoinPoint能调用连接点所表示的方法 pjp.proceed()
            System.out.println(pjp.getSignature().getName()+" is start..");
            
            //调用目标对象中的方法
            Object obj = pjp.proceed();
            
            System.out.println(pjp.getSignature().getName()+" is end..");
            return obj;
        }
        
        public void throwingTest(JoinPoint p,Exception ex){
            System.out.println(p.getSignature().getName()+" is throwing..."+ex.getMessage());
        }
    }
    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        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
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
        
        <bean name="account" class="com.cnblogs.aop.pojo.Account">
            <property name="id" value="6217" />
            <property name="name" value="jack" />
            <property name="balance" value="1000.00" />
        </bean>
        
        <!-- 目标对象 -->
        <bean name="service" class="com.cnblogs.aop.service.impl.BankServiceImpl">
            <property name="account" ref="account" />
        </bean>
        
        <!-- 切面对象 -->
        <bean name="log" class="com.cnblogs.aop.aopConfig.XmlHandler">
        </bean>
        
        <!-- 配置aop的代理 -->
        <aop:config>
            <!-- 定义切入点名为myPointCut -->
            <aop:pointcut expression="execution(public * com.cnblogs.aop.service.*.*(..))" 
                        id="myPointCut"/>
            
            <!-- 定义切面类 以及需要使用的advice -->
            <aop:aspect id="aspect" ref="log">
                <!-- before表示会把切面类log中的beforeTest方法织入到名字
                       叫myPointCut的切入点前面 
                -->
                <aop:before method="beforeTest" pointcut-ref="myPointCut"/>
    
                <!-- after表示不管方法是否正常结束都会起作用 -->
                <aop:after method="afterTest" pointcut-ref="myPointCut"/>
    
                <!-- after-returning表示方法正常结束才会起作用(抛异常时候不起作用) -->
                <aop:after-returning method="afterReturningTest" pointcut-ref="myPointCut"/>
    
                <aop:around method="aroundTest" pointcut-ref="myPointCut"/>
    
                <!-- throwing="ex"表示throwingTest方法中接收异常对象的名字一定要是ex -->
                <aop:after-throwing method="throwingTest" pointcut-ref="myPointCut" throwing="ex"/>
    
            </aop:aspect>
        </aop:config>
    </beans>
    @Test
        public void aop2(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/aopConfig/config2.xml";
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                BankService target = (BankService) container.getBean("service");
                target.save(1000.0);
    //            target.withdraw(20000.0);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    save before...
    save is start..
    存钱: 1000.0
    save is end..
    save afterReturning
    save after...

     六:使用注解配置AOP

     1.定义切面类:

    package com.cnblogs.aop.annotation;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    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;
    
    @Component
    @Aspect
    public class AnnotationHandler {
     
        /**
         * 在一个方法上面加上注解来定义切入点
         * 这个切入点的名字就是这个方法的名字
         * 这个方法本身不需要有什么作用
         * 这个方法的意义就是:给这个 @Pointcut注解一个可以书写的地方
         * 因为注解只能写在方法、属性、类的上面,并且方法名作为切入点的名字
         */
        @Pointcut("execution(public * com.cnblogs.aop.service.impl.*.*(..))")
        public void myPoint(){}
        
        /**
         * 前置通知
         * 如果需要用到Joinpoint,可以在方法参数加上
         * @param j
         */
        @Before("myPoint()")
        public void beforeTest(){
            System.out.println("前置通知: " + new SimpleDateFormat().format(new Date()));
        }
        
        /**
         * 最终通知
         */
        @After("myPoint()")
        public void afterTest(){
            System.out.println("最终通知: " + new SimpleDateFormat().format(new Date()));
        }
        
        /**
         * 环绕通知
         * @throws Throwable 
         */
        @Around("myPoint()")
        public void aroundTest(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("环绕通知之前置: " + 
                    new SimpleDateFormat().format(new Date()));    
            
            //调用连接点的方法去执行
            Object obj = pjp.proceed();
            
            System.out.println("环绕通知之后置: " + 
                    new SimpleDateFormat().format(new Date()));    
        }
        
        /**
         * 成功返回才会执行的后置通知
         */
        @AfterReturning("myPoint()")
        public void afterReturningTest(){
            System.out.println("成功返回才执行的后置通知: " + new SimpleDateFormat().format(new Date()));    
        }
        
        /**
         * 抛出异常通知
         */
        @AfterThrowing(value="myPoint()",throwing="ex")
        public void throwTest(Exception ex){
            System.out.println("抛出异常才会执行的通知(1参): " + 
                    ex.toString());    
        }
    }

    2.在BankServiceImpl上加 @Service("service") 注解,在account成员变量加 @Autowired注解。

    在Account上加  @Component @Scope("prototype")。

     

    3.在xml文件中加上

       <aop:aspectj-autoproxy/>
        <context:component-scan base-package="com.cnblogs.aop.pojo"/>
        <context:component-scan base-package="com.cnblogs.aop.service.*"/>
        <context:component-scan base-package="com.cnblogs.aop.annotation"/>

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

    表示SpringIOC容器会扫描此包下加了@Component,@Service注解的类,并放入IOC容器。在上上篇随便springIOC的三种注入方式

    有介绍。

     

    4.最后一步就是测试了

    @Test
        public void annotation(){
            try {
                // 获取springIOC容器
                String path = "com/cnblogs/aop/annotation/annotation.xml";
                ApplicationContext container = new ClassPathXmlApplicationContext(path);
                
                BankService service = (BankService) container.getBean("service");
    //            service.getAccount().setBalance(10000.0);
                
                service.save(3000.0);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    环绕通知之前置: 19-5-29 下午8:41
    前置通知: 19-5-29 下午8:41
    存钱: 3000.0
    环绕通知之后置: 19-5-29 下午8:41
    最终通知: 19-5-29 下午8:41
    成功返回才执行的后置通知: 19-5-29 下午8:41

     

  • 相关阅读:
    命令行添加subl命令
    mac 本地跨域
    ionic2 处理android硬件返回按钮
    ionic2 (真正)修改应用图标和启动画面
    ionic2 隐藏滚动条
    Tomcat在Eclips中的使用及注意细节
    JAVA解析xml的四种方式比较
    程序人生咖啡馆
    浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制
    mySQL 的 2个分类
  • 原文地址:https://www.cnblogs.com/yangji0202/p/10939706.html
Copyright © 2011-2022 走看看