zoukankan      html  css  js  c++  java
  • spring aop advice

    1.前置通知(BeforeAdvice):

    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    public class HelloBeforeAdvice implements MethodBeforeAdvice{
    
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("do something before some method");
        }
    }

    如果出现 The hierarchy of the type HelloBeforeAdvice is inconsistent错误 , 是缺少相关的jar或者jdk环境不对。

    2.后置通知(AfterAdvice)

    import java.lang.reflect.Method;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    public class HelloAfterAdvice implements AfterReturningAdvice {
    
        public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("do something after the method");
        }
    
    }

    3. Test client

    import org.springframework.aop.framework.ProxyFactory;
    
    public class TestClient {
    
        public static void main(String[] args) {
            ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
            proxyFactory.setTarget(new HelloImpl());
            proxyFactory.addAdvice(new HelloBeforeAdvice());
            proxyFactory.addAdvice(new HelloAfterAdvice());
            Hello helloProxy = (Hello) proxyFactory.getProxy();
            System.out.println(helloProxy.sayHello("zhangsan"));
        }
    }

    ----------------------------
    public class HelloImpl implements Hello {

        public String sayHello(String str) {
            System.out.println("hello>>>"+str);
            return "hello>>>"+str;
        }
    }

    4. 环绕通知(AroundAdvice)

    //aop联盟提供的接口
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class HelloAroundAdvice implements MethodInterceptor {
    
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before method>>>");
            Object result = invocation.proceed();
            System.out.println("after method>>");
            return result;
        }
    }

    5. spring 配置文件方式

     <?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" xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
           xmlns:util="http://www.springframework.org/schema/util" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
           xmlns:cache="http://www.springframework.org/schema/cache"
           xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/jdbc
        http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/cache
        http://www.springframework.org/schema/cache/spring-cache.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd">
     <!-- 自动扫描ppms包 ,将带有注解的类 纳入spring容器管理 -->
        <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
    
        <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.cdv.ppms.Hello"/><!-- 需要代理的接口 -->
            <property name="target" ref="helloImpl"/><!-- 接口实现类 , 此处需要是ref 否则object is not an instance of declaring class-->
            <property name="interceptorNames"><!-- 拦截器 名称-->
                <list>
                    <value>helloAroundAdvice</value>
                </list>
            </property>
        </bean>
       <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
    </beans>
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloImpl implements Hello {
    //...
    }
    
    @Component
    public class HelloAroundAdvice implements MethodInterceptor {
    //....
    }
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Hello    hello = (Hello) context.getBean("helloProxy");
            hello.sayHello("test xml");
        }
    }

     6. 抛出通知(ThrowAdvice)

    import java.lang.reflect.Method;
    import org.springframework.aop.ThrowsAdvice;
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloThrowAdvice implements ThrowsAdvice{
        public void afterThrowing(Method method, Object[] args, Object target, Exception e){
            System.out.println("-------------------Throw Exception------------------------");
            System.out.println("target class>>"+target.getClass().getName());
            System.out.println("method class>>" + method.getName());
            System.out.println("exception message>>"+e.getMessage());
            System.out.println("-------------------Throw Exception------------------------");
        }
    }
    <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.cdv.ppms.Hello"/><!-- 需要代理的接口 -->
            <property name="target" ref="helloImpl"/><!-- 接口实现类 -->
            <property name="interceptorNames"><!-- 拦截器 名称-->
                <list>
                    <value>helloAroundAdvice</value>
                    <value>testThrowAdvice</value>
                </list>
            </property>
        </bean>
    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloImpl implements Hello {
    
        public String sayHello(String str) {
            System.out.println("hello>>>"+str);
            throw new RuntimeException("error>>");//加入异常以便测试
        }
    }

    7. 引入通知(Introduction Advice)

    对方法的增强叫织入(Weaving),对类的增强叫引入(Introduction)

    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.support.DelegatingIntroductionInterceptor;
    import org.springframework.stereotype.Component;
    @Component
    public class HelloIntroAdvice extends DelegatingIntroductionInterceptor implements Apology {
    
        /**
         * 
         */
        private static final long serialVersionUID = -3328637206414010549L;
        
        public Object invoke(MethodInvocation invocation) throws Throwable{
            
            return super.invoke(invocation);
        }    
    
        public void saySorry(String str) {
            System.out.println("introduction advice>>>"+str);
        }
    }
        <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces" value="com.cdv.ppms.Apology"/><!-- 需要代理的接口 -->
            <property name="target" ref="helloImpl"/><!-- 接口实现类 -->
            <property name="interceptorNames"><!-- 拦截器 名称-->
                <list>
                    <value>helloAroundAdvice</value>
                    <value>helloThrowAdvice</value>
                    <value>helloIntroAdvice</value>
                </list>
            </property>
            <property name="proxyTargetClass" value="true"></property><!--代理目标类, 默认false代理接口-->
        </bean>
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloProxy");
            helloImpl.sayHello("test xml");
            Apology apology = (Apology) helloImpl;//将目标类向上强制转型为Apology接口, 这时引入通知给我们带来的特性, 也就是“接口动态实现”功能
            apology.saySorry("zhangsan");
        }
    }

    8. 切面(Advisor)

    切面(Advisor)封装了通知(Advice)和切点(Pointcut)

    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloImpl implements Hello {
    
        public String sayHello(String str) {
            System.out.println("hello>>>"+str);
            return str;
        }
        
        public void advisorOne(String name){
            System.out.println("advisor one>>>"+name);
        }
        
        public void advisorTwo(String name){
            System.out.println("advisor two>>"+name);
        }
    }

    在代理类中新增以advisor开头的方法,对其进行拦截

    <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
        <!--定义切面-->
        <bean id="helloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="helloAroundAdvice"/>
            <property name="pattern" value="com.cdv.ppms.HelloImpl.advisor.*"></property><!-- 切点 (正则表达式) -->
        </bean>
        <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="helloImpl"/><!-- 接口实现类 -->
            <property name="interceptorNames" value="helloAdvisor"/>
            <property name="proxyTargetClass" value="true"></property>
        </bean>
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
        <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.stereotype.Component;
    @Component
    public class HelloAroundAdvice implements MethodInterceptor {
    
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("before method>>>");
            Object result = invocation.proceed();
            System.out.println("after method>>");
            return result;
        }
    }
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloProxy");
            helloImpl.advisorOne("11111111");
            helloImpl.advisorTwo("2222222222");
    
        }
    }
    -------------around包了两层, 有问题-------------------------------------------
    before method>>>
    before method>>>
    advisor one>>>11111111
    after method>>
    after method>>
    before method>>>
    before method>>>
    advisor two>>2222222222
    after method>>
    after method>>
    ------------------------

    随着项目的扩大, 上述的代理配置会越来越多。

    9. 自动代理(扫描bean名称)

     定义BeanNameAutoProxyCreator,该bean是个bean后处理器,无需被引用,因此没有id属性

     <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
    
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
            <property name="beanNames" value="*Impl"/><!--对以Impl结尾的类进行处理-->
            <property name="interceptorNames" value="helloAroundAdvice"/>
            <property name="optimize" value="true"/>
        </bean>
    
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
        <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>

    测试

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloImpl");
            helloImpl.advisorOne("11111111");//
            helloImpl.advisorTwo("2222222222");
            helloImpl.sayHello("zhangwuji");
        }
    }

    10. 自动代理(扫描切面配置)

    代理将由DefaultAdvisorAutoProxyCreator自动生成,这个类可以扫描所有的切面类

        <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
        <bean id="helloAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="helloAroundAdvice"/>
            <property name="pattern" value="com.cdv.ppms.HelloImpl.advisor.*"/>
        </bean>
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
            <property name="optimize" value="true"/>
        </bean>
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>
        <bean id="helloAroundAdvice" class="com.cdv.ppms.HelloAroundAdvice"></bean>

    测试

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloImpl");
            helloImpl.advisorOne("11111111");
            helloImpl.advisorTwo("2222222222");
            helloImpl.sayHello("zhangwuji");
        }
    }
    //只有符合advisor开头的方法才会被代理

    这样在spring的配置中会存在大量的切面配置(想拦截指定注解的方法,就必须拓展DefaultPointcutAdvisor类,自定义一个切面类)。

    11. Spring & AspectJ

    11-1 基于注解 :通过execution()表达式 拦截方法

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class HelloAspect {
    
        @Around("execution(* com.cdv.ppms.HelloImpl.*(..))")//切点表达式
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            before();
            Object result = pjp.proceed();
            after();
            return result;
        }
    
        private void after() {
            System.out.println("hey,, after.");
            
        }
    
        private void before() {
            System.out.println("hey,, before");        
        }
    }

    切点表达式

    execution()表示拦截的方法, 挂号中定义拦截的规则

    上面第一个*表示返回任意类型

    第二个*表示HelloImpl类中的任意方法

    ..表示任意类型参数

        <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
        <aop:aspectj-autoproxy proxy-target-class="true"/><!-- 默认值为false只能代理接口,当为true时才能代理目标类 -->
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>

    test

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloImpl");
            helloImpl.advisorOne("11111111");
            helloImpl.advisorTwo("2222222222");
            helloImpl.sayHello("zhangwuji");
        }
    }

    11-2 基于注解 通过AspectJ @annotation表达式拦截方法

    自定义注解

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyTag {
    
    }

    更改aspect类的切点表达式

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class HelloAspect {
    
        @Around("@annotation(com.cdv.ppms.MyTag)")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            before();
            Object result = pjp.proceed();
            after();
            return result;
        }
    
        private void after() {
            System.out.println("hey,, after.");
            
        }
    
        private void before() {
            System.out.println("hey,, before");        
        }
    }

    该注解可以标注在方法上,运行时生效

    import org.springframework.stereotype.Component;
    
    @Component
    public class HelloImpl implements Hello {
    
        public String sayHello(String str) {
            System.out.println("hello>>>"+str);
            return str;
        }
        @MyTag
        public void advisorOne(String name){
            System.out.println("advisor one>>>"+name);
        }
        public void advisorTwo(String name){
            System.out.println("advisor two>>"+name);
        }
    }

    配置

     <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>

    测试

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloImpl");
            helloImpl.advisorOne("11111111");
            helloImpl.advisorTwo("2222222222");
            helloImpl.sayHello("zhangwuji");
        }
    }

     其他几个注解

    @Before  @After  @AfterThrowing @DeclareParents引入通知 AfterReturning(返回后通知,在方法结束后执行)

    11-3 引入通知

    Aspect类

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.DeclareParents;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class HelloAspect {
        //something need to do
        @DeclareParents(value="com.cdv.ppms.HelloImpl",defaultImpl=com.cdv.ppms.ApologyImpl.class)
        private Apology apology;
    
    }

    接口实现类

    public class ApologyImpl implements Apology {
    
        public void saySorry(String str) {
            System.out.println("apologyImpl>>>"+str);
        }
    }

    配置

        <context:component-scan base-package="com.cdv.ppms"></context:component-scan>
        <bean id="helloImpl" class="com.cdv.ppms.HelloImpl"></bean>

    测试

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestXMLClient {
    
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloImpl    helloImpl = (HelloImpl) context.getBean("helloImpl");
            helloImpl.sayHello("zhangwuji");
            Apology apology = (Apology) helloImpl;
            apology.saySorry("hahahahah");
        }
    }
  • 相关阅读:
    调用Type.InvokeMember()时出现MissingMethodException
    C#学习之Delegate
    WCF之元数据交换 (Metadata Exchange)
    定义Enum的开始和结束,这样就能循环Enum了
    定制自己的Visual Studio的Debugger Visualizer
    C#中 #if DEBUG 和 Conditional("DEBUG")的区别
    从哪里开始学习Windows 8?(zz)
    Macro 小总结
    WPF应用的图标
    如何把 Visutal studio中的“printonbreakpoint”消息打印在程序的任何地方
  • 原文地址:https://www.cnblogs.com/rocky-fang/p/5459346.html
Copyright © 2011-2022 走看看