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");
    }
}
原文地址:https://www.cnblogs.com/rocky-fang/p/5459346.html