SpringAOP在web應用中的使用

以前的aop是經過手動建立代理類來進行通知的,可是在平常開發中,咱們並不肯意在代碼中硬編碼這些代理類,咱們更願意使用DI和IOC來管理aop代理類。Spring爲咱們提供瞭如下方式來使用aop框架html

1、以聲明的方式配置AOP(就是使用xml配置文件)

1.使用ProxyFactoryBean的方式:

ProxyFactoryBean類是FactoryBean的一個實現類,它容許指定一個bean做爲目標,而且爲該bean提供一組通知和顧問(這些通知和顧問最終會被合併到一個AOP代理中)它和咱們以前的ProxyFactory都是Advised的實現。java

如下是一個簡單的例子:一個學生和一個老師,老師會告訴學生應該作什麼。git

public class Student {

    public void talk() {
        System.out.println("I am a boy");
    }

    public void walk() {
        System.out.println("I am walking");
    }

    public void sleep() {
        System.out.println("I want to sleep");
    }
}

老師類github

public class Teacher {

    private Student student;

    public void tellStudent(){
        student.sleep();
        student.talk();
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}
package cn.lyn4ever.aop;

import org.aspectj.lang.JoinPoint;

public class AuditAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
        System.out.println("這個方法被通知了" + method.getName());
    }
}
  • 而後就使用spring的IOC來管理這個通知類,在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:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       https://www.springframework.org/schema/util/spring-util.xsd">

    <!--注入student-->
    <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student">
    </bean>

    <!--注入teacher-->
    <bean name="teacher" class="cn.lyn4ever.aop.aopconfig.Teacher">
        <!--注意,這個student的屬性要是上邊的代理類,而不是能student-->
        <!--<property name="student" ref="student"/>-->
        <property name="student" ref="proxyOne"/>
    </bean>

    <!--注入咱們建立的通知類-->
    <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean>

    <!--建立代理類,使用前邊寫的通知進行通知,這樣會使這個類上的全部方法都被通知-->
    <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student"
          p:interceptorNames-ref="interceptorNames">
        <!--由於interceptorNames的屬性是一個可變參數,也就是一個list-->
    </bean>

    <!--在上邊引入了util的名稱空間,簡化了書寫-->
    <util:list id="interceptorNames">
        <value>advice</value>
    </util:list>
</beans>
  • 測試類
public static void main(String[] args) {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.load("application1.xml");
        context.refresh();

        Teacher teacher = (Teacher) context.getBean("teacherOne");
        teacher.tellStudent();

    }
  • 運行結果沒有問題
    SpringAop在web應用中的使用web

  • 以上是經過直接建立通知的方式,接下來咱們試一個建立一個切入點(由於以上是對類中全部方法都進行通知,這時咱們使用切入點只對其中部分方法進行通知),在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:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       https://www.springframework.org/schema/util/spring-util.xsd">

    <!--注入student-->
    <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student">
    </bean>

    <!--注入teacher-->
    <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher">
        <!--注意,這個student的屬性要是上邊的代理類,而不是能student-->
        <!--<property name="student" ref="student"/>-->
        <property name="student" ref="proxyOne"/>
    </bean>

    <!--注入咱們建立的通知類-->
    <bean id="advice" class="cn.lyn4ever.aop.aopconfig.AuditAdvice"></bean>

    <!--建立代理類,使用前邊寫的通知進行通知,這樣會使這個類上的全部方法都被通知-->
    <bean name="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean" p:target-ref="student"
          p:interceptorNames-ref="interceptorNames">
        <!--由於interceptorNames的屬性是一個可變參數,也就是一個list-->
    </bean>

    <!--在上邊引入了util的名稱空間,簡化了書寫-->
    <util:list id="interceptorNames">
        <value>advice</value>
    </util:list>


    <!--如下是使用切入點的方式來進行通知,上邊的代碼和上一個配置文件同樣,沒有修改-->
    <!--sutdent基本bean,咱們繼續使用-->

    <bean name="teacherTwo" p:student-ref="proxyTwo" class="cn.lyn4ever.aop.aopconfig.Teacher"/>

    <bean id="proxyTwo" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:target-ref="student" p:interceptorNames-ref="interceptorAdvisorNames">
    </bean>

    <util:list id="interceptorAdvisorNames">
        <value>advisor</value>
    </util:list>

    <!--配置切入點bean-->
    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
          p:advice-ref="advice">
        <property name="pointcut">
            <!--這個切入點咱們用了一個匿名bean來寫aspectJ的表達式,固然也能夠用其餘的類型切入點,這個在上邊連接中能看到-->
            <bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut"
                  p:expression="execution(* talk*(..))"/>
        </property>

    </bean>

</beans>

SpringAop在web應用中的使用

  • 上圖中的那個aspectj表達式寫錯了,在代碼中有正確的
    title

2.使用aop名稱空間

在xml中引入以下的名稱空間,爲了避免被影響,我冊了其餘多餘的名稱空間。而後很普通地注入咱們以前那三個beanspring

<?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"
       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.xsd
  ">

    <!--經過普通的方式來注入三個bean-->
    <!--注入student-->
    <bean name="student" class="cn.lyn4ever.aop.aopconfig.Student"/>
    <!--注入teacher-->
    <bean name="teacherOne" class="cn.lyn4ever.aop.aopconfig.Teacher">
        <property name="student" ref="student"/>
    </bean>
    <!--注入咱們建立的通知類-->
    <bean id="advice" class="cn.lyn4ever.aop.proxyfactory.BeforeAdvice"/>


    <aop:config>
        <aop:pointcut id="talkExecution" expression="execution(* talk*(..))"/>
        <aop:aspect ref="advice">
            <!--這個方法就是咱們在自定義通知類中之寫的方法-->
            <aop:before method="beforeSaySomething" pointcut-ref="talkExecution"/>
            <!--固然,還能夠配置其餘通知類型-->
        </aop:aspect>
    </aop:config>



</beans>

在這個配置中,咱們還能夠配置其餘類型的通知,可是這個method屬性必定要寫咱們自定義的那個通知類中的方法express

SpringAop在web應用中的使用

  • 在aop:pointcut中寫expression時還支持以下語法:
<aop:pointcut id="talkExecution" expression="execution(* talk*(..)) and args(String) and bean(stu*)"/>
<!--
中間的這個and表示和,也能夠用or來表示或
args(String) 意思是參數類型是string,也但是自定義的類,這個後邊有例子
bean(stu*) 意思是bean的id是以stu開頭的,經常使用的就是用bean(*Service*)來表示服務層的bean
-->

3.使用@AspectJ樣式註解方式

雖然是經過註解的方式來聲明註解類,可是仍是須要在xml中配置一點點內容(經過註解的方式也能夠配置,可是在springboot中要使用的話有更方便的方式)springboot

  • 爲了方便,就只寫了一個HighStudent,並且直接調用它的方法,不依賴於外部的teacher實例來調用
package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.stereotype.Component;

/**
 * 聲明這是一個SpringBean,由Spring來管理它
 */
@Component
public class HighStudent {

    public void talk() {
        System.out.println("I am a boy");
    }

    public void walk() {
        System.out.println("I am walking");
    }

    /**
     * 這個方法添加一個teacher來作爲參數,爲了配置後邊切入點中的args()
     * @param teacher
     */
    public void sleep(Teacher teacher) {
        System.out.println("I want to sleep");
    }
}
  • 建立切面類
package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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 //聲明交由spring管理
@Aspect //表示這是一個切面類
public class AnnotatedAdvice {

    /*
    建立切入點,固然也能夠是多個
     */
    @Pointcut("execution(* talk*(..))")
    public void talkExecution(){}

    @Pointcut("bean(high*)")//這裏爲何是high,由於咱們這回測試bean是highStudent
    public void beanPoint(){}

    @Pointcut("args(value)")
    public void argsPoint(Teacher value){}

    /*
    建立通知,固然也能夠是多個
    這個註解的參數就是上邊的切入點方法名,注意有的還帶參數
    這個通知方法的參數和以前同樣,榀加JoinPoint,也可不加
     */
    @Before("talkExecution()")
    public void doSomethingBefore(JoinPoint joinPoint){
        System.out.println("before: Do Something"+joinPoint.getSignature().getName()+"()");
    }

    /**
     * 環繞通知請加上ProceedingJoinPoint參數 ,它是joinPoint的子類
     * 由於你要放行方法的話,必需要加這個
     * @param joinPoint
     * @param teacher
     */
    @Around("argsPoint(teacher) && beanPoint()")
    public Object doSomethindAround(ProceedingJoinPoint joinPoint, Teacher teacher) throws Throwable {
        System.out.println("Around: Before Do Something"+joinPoint.getSignature().getName()+"()");
        Object proceed = joinPoint.proceed();
        System.out.println("Around: After Do Something"+joinPoint.getSignature().getName()+"()");

        return proceed;
    }

}
  • 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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--通知Spring掃描@Aspect註解-->
    <aop:aspectj-autoproxy/>

    <!--配置掃描包,掃描@Component-->
    <context:component-scan base-package="cn.lyn4ever.aop.aspectj"/>

</beans>
  • 使用Java註解配置的方式配置掃描註解
@Configuration //聲明這是一個配置類
@ComponentScan("cn.lyn4ever.aop.aspectj")
@EnableAspectJAutoProxy(proxyTargetClass = true)//至關於xml中的<aop:aspectj-autoproxy/>
public class BeanConfig {
}
  • 測試方法
package cn.lyn4ever.aop.aspectj;

import cn.lyn4ever.aop.aopconfig.Teacher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class AspectMain {
    public static void main(String[] args) {
//        xmlConfig();
        javaConfig();

    }

    private static void javaConfig() {
        GenericApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        HighStudent student = (HighStudent) context.getBean("highStudent");
        student.sleep(new Teacher());//應該被環繞通知
        System.out.println();

        student.talk();//前置通知
        System.out.println();

        student.walk();//不會被通知
        System.out.println();
    }

    private static void xmlConfig(){
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.load("application_aspect.xml");
        context.refresh();

        HighStudent student = (HighStudent) context.getBean("highStudent");
        student.sleep(new Teacher());//應該被環繞通知
        System.out.println();

        student.talk();//前置通知
        System.out.println();

        student.walk();//不會被通知
        System.out.println();
    }
}

SpringAOP在web應用中的使用

項目代碼地址,若是以爲還不錯的話,給個star吧app

相關文章
相關標籤/搜索