【FunnyBear的Java之旅 - Spring篇】Spring AOP

Spring AOP实践

要使用Spring的切片(Aspect),首先要在Maven项目中添加以下依赖,然后我们可以按XMLAnnotation(标注)的方法,进行AOP的配置。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.funnybear.springaop</groupId>
    <artifactId>springaop</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.0.RELEASE</version>
        </dependency>
    </dependencies>
</project>

按XML配置

1. 新建Camera类。我们想在这个类执行操作时,输出日志,但不改动类本身的代码。

package com.funnybear.springaop;

public class Camera {
    public void snap(){
        System.out.println("snap!");
    }
}

2. 新建Logger类。这个类就是我们的切片(Aspect)

package com.funnybear.springaop;

public class Logger {
    public void aboutToTakePhoto(){
        System.out.println("about to snap...");
    }
}

3. 在beans.xml中,注册Camera和Logger类,并添加AOP配置

其中,<aop:pointcut>是用来指定我们关心的类方法;<aop:aspect>用来指定充当切片的类;<aop:before>是我们想在pointcut所指定的方法执行前,运行切片类中的指定方法。

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

    <bean id="camera" class="com.funnybear.springaop.Camera"></bean>
    <bean id="logger" class="com.funnybear.springaop.Logger"></bean>

    <aop:config>
        <aop:pointcut expression="execution(void com.funnybear.springaop.Camera.snap())"
            id="camerasnap" />
        <aop:aspect ref="logger" id="loggeraspect">
            <aop:before method="aboutToTakePhoto" pointcut-ref="camerasnap" />
        </aop:aspect>
    </aop:config>
</beans>

4. 从ApplicationContext中取出 Camera类,并执行。

package com.funnybear.springaop;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/funnybear/springaop/beans.xml");
        Camera camera = (Camera)context.getBean("camera");
        camera.snap();
        context.close();
    }
}

 

 按Annotation配置

1. 新建Camera类,@Component把它标记为一个可导出的bean

package com.funnybear.springaop;

import org.springframework.stereotype.Component;

@Component("camera")
public class Camera {
    public void snap(){
        System.out.println("snap!");
    }
}

2. 新建Logger类,@Aspect把它标记为一个切片,@Component把它标记为一个bean

@Pointcut指定我们关心的,想对其添加切片的方法。这样单独指定,是为了方便复用切片入口

@Before指定我们的切片方法

package com.funnybear.springaop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component("logger")
public class Logger {
    
    @Pointcut("execution(void com.funnybear.springaop.Camera.snap())")
    public void cameraSnap(){
    }
    
    @Before("cameraSnap()")
    public void aboutToTakePhoto(){
        System.out.println("about to snap...");
    }
}

3. 在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/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">
    <context:component-scan base-package="com.funnybear.springaop"></context:component-scan>
    <context:annotation-config></context:annotation-config>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

4. 从ApplicationContext中取出 Camera类,并执行。

package com.funnybear.springaop;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/funnybear/springaop/beans.xml");
        Camera camera = (Camera)context.getBean("camera");
        camera.snap();
        context.close();
    }
}

深入:

1. Pointcut(切片入口)匹配

第一个星号(*)匹配任意返回值,第二个星号(*)匹配Camera类的所有方法名,(..)匹配任意参数

类似地,我们可以实现很灵活的模糊匹配

@Pointcut("execution(* com.funnybear.springaop.Camera.*(..))")

也可以用within来匹配包或类中的所有方法

@Pointcut("within(com.funnybear.springaop..*)")
@Pointcut("within(com.funnybear.springaop.Camera)")

匹配被特定Annotaion标记的

@Pointcut("within(@org.springframework.stereotype.Component com.funnybear.springaop..*)")

匹配被特定Annotation标记的方法

@Pointcut("@annotation(java.lang.Deprecated)")

匹配特定id为"camera"的bean

@Pointcut("bean(camera)")

匹配接受特定参数的方法。".."匹配任意数量的任意参数,"*"匹配单个任意参数

@Pointcut("args(int, ..)")
@Pointcut("args(int, double, *)")

2. 获取被切片方法的参数

方法一:为切片方法添加JoinPoint参数。我们无法或者参数的类型。

package com.funnybear.springaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component("logger")
public class Logger {
    
    @Pointcut("within(com.funnybear.springaop.Camera)")
    public void cameraSnap(){
    }
    
    @Before("cameraSnap()")
    public void beforeAdvice(JoinPoint jp){
        System.out.println("before advice ...");
        
        for(Object obj : jp.getArgs()){
            System.out.println("arg: " + obj);
        }
    }
}

方法二:为用args来匹配要切片的方法,并添加参数占位符,显示指定参数类型。

package com.funnybear.springaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component("logger")
public class Logger {
    
    @Pointcut("args(exposure)")
    public void cameraSnap(int exposure){
    }
    
    @Before("cameraSnap(exposure)")
    public void beforeAdvice(JoinPoint jp, int exposure){
        System.out.println("before advice ...");
        System.out.println("exposure:" + exposure);
    }
}

3. 合并切片入口

可以用多个切片入口去联合指定切面方法,也可以在定义一个切面入口时,结合多个条件。

package com.funnybear.springaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component("logger")
public class Logger {
    
    @Pointcut("within(com.funnybear.springaop.Camera)")
    public void camera(){
    }
    
    @Pointcut("args(exposure)")
    public void cameraSnap(int exposure){
    }
    
    @Before("camera() && cameraSnap(exposure)")
    public void beforeAdvice(JoinPoint jp, int exposure){
        System.out.println("before advice ...");
        System.out.println("exposure:" + exposure);
    }
}

4. Aspect Proxy 切片代理

从Application Context中取出的Camera对象,是由Spring Aspect拓展过的Camera的子类。我们添加的切片方法,最终被添加到了这个子类中。

 

转载于:https://www.cnblogs.com/funnybear/p/5939929.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值