前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring AOP的三种实现方式

Spring AOP的三种实现方式

作者头像
week
发布2018-08-24 14:34:59
1.7K0
发布2018-08-24 14:34:59
举报
文章被收录于专栏:用户画像用户画像

一、浅析AOP

参考:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-api

AOP意味面向切面编程,是通过预编译方式和运行期动态代理,实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

1、方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。

2、连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvic

3、切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

4、引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口

5、目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO

AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

6、织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

二、AOP项目预览

1、有图有真相

2、 github源码:https://github.com/jxq0816/spring-aop-demo

3、代码组织结构

三、基础配置

1、 pom.xml

代码语言:javascript
复制
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.week7i.share</groupId>
  <artifactId>spring-aop-demo</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>spring-aop-demo Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <properties>
    <spring.version>4.3.6.RELEASE</spring.version>
  </properties>
  <dependencies>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!--JSP Standard Tag Library,JSP标准标签库-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!--AOP-->

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.13</version>
    </dependency>

    <!--Code Generation Library 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口
    它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)-->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.4</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
      <version>4.10</version>
    </dependency>
    
  </dependencies>
  <build>
    <finalName>spring-aop-demo</finalName>
  </build>
</project>

2、web.xml

注意:aop的配置文件必须放在servlet的param-value中,才可以在springmvc上AOP,最初放在了context-param的param-value中,折腾了一天才搞定。

代码语言:javascript
复制
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <context-param>
   <!-- applicationContext.xml默认地址目录是/WEB-INF-->
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath:/applicationContext.xml
    </param-value>
  </context-param>

  <!-- 加入ContextLoaderListener 启动Web容器时,自动装配ApplicationContext的配置信息-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--DispatcherServlet是前置控制器,拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理-->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
        classpath:/dispatcherServlet.xml
        classpath:/dispatcherServlet-aop-01.xml
        classpath:/dispatcherServlet-aop-02.xml
        classpath:/dispatcherServlet-aop-03.xml
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>/index.jsp</welcome-file>
  </welcome-file-list>
</web-app

四、 AOP方法01

配置文件定义Advice通知对象、Aspect切面、Pointcut切点,并通过定义aop:after连接Adivicef通知方法和Pointcut切点

1、 dispatcherServlet-aop-01.xml

代码语言:javascript
复制
<?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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.weeking.share"/>

    <!-- 被代理对象 -->
    <bean id="math" class="com.weeking.share.service.aop01.MathUtils"></bean>
    <!-- 切面 -->
    <bean id="aspect" class="com.weeking.share.service.aop01.AspectProxy"></bean>

    <aop:config proxy-target-class="true">
        <!-- 切点 -->
        <aop:pointcut id="pointCut" expression="execution(public * com.weeking.share.service.aop01.MathUtils.*(..))" />
        <!--切面 -->
        <aop:aspect ref="aspect">
            <!--连接通知方法与切点 -->
            <aop:before method="before" pointcut-ref="pointCut"/>
            <aop:after method="after" pointcut-ref="pointCut"/>
        </aop:aspect>

    </aop:config>
    
</beans>

2、 AspectProxy.java

代码语言:javascript
复制
package com.weeking.share.service.aop01;

import org.aspectj.lang.JoinPoint;

/**
 * 切面类,其方法为通知
 *
 */
public class AspectProxy {

    public void before(JoinPoint jp){
        System.out.println("----------AOP01 before execute----------");
        System.out.println(jp.getSignature().getName());
    }

    public void after(JoinPoint jp){
        System.out.println("----------AOP01 after execute----------");
    }
}

3、MathUtils.java

代码语言:javascript
复制
package com.weeking.share.service.aop01;

/**
 * 被代理的目标类
 */
public class MathUtils{
    //加
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }

    //减
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }

    //乘
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }

    //除
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}

4、测试方法

代码语言:javascript
复制
 @Test
    public void testAop01() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-01.xml");
        MathUtils math = ctx.getBean("math", MathUtils.class);
        int n1 = 100, n2 = 5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }

5、运行结果

五、AOP02 注解实现

1、 dispatcherServlet-aop-02.xml

代码语言:javascript
复制
<?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-4.0.xsd
               http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.weeking.share"/>

    <aop:config>

        <!--定义切点-->
        <aop:pointcut
                expression="execution(public * com.weeking.share.service.aop02.service.LoginService.*(..)) and @annotation(com.weeking.share.service.aop02.annotion.Operate)"
                id="pointCut"/>
        <!--定义切面 当执行同一个切入点时,不同切面的执行先后顺序是由“每个切面的order属性”而定的.
        order越小,则该该切面中的通知越先被执行。-->
        <aop:aspect  ref="aspectProxy02" order="1">
            <!--连接通知方法与切点-->
            <aop:after method="doAfter" pointcut-ref="pointCut" />
        </aop:aspect>

    </aop:config>

    <bean id="aspectProxy02" class="com.weeking.share.service.aop02.AspectProxy02"></bean>
</beans>

2、Operate.java

代码语言:javascript
复制
package com.weeking.share.service.aop02.annotion;

import com.weeking.share.service.aop02.ennum.OperateEnum;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target(ElementType.METHOD)
public @interface Operate {
	public OperateEnum status();
}

3、OperateEnum.java

代码语言:javascript
复制
package com.weeking.share.service.aop02.ennum;


public enum OperateEnum {

	USERNAME("username");
	private String type;
    OperateEnum(String type) {
        this.type = type;
    }

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}


}

4、AspectProxy02.java

代码语言:javascript
复制
package com.weeking.share.service.aop02;

import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

public class AspectProxy02 {

	public void doAfter(JoinPoint jp) throws NoSuchMethodException, SecurityException {
		System.out.println("---------- AOP02 after execute ----------");
		MethodSignature signature = (MethodSignature) jp.getSignature();
		Method method = signature.getMethod();
		System.out.println("method : "+method.getName());

		Method realMethod = jp.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
		System.out.println("realMethod : "+method.getName());

		Operate operate = realMethod.getAnnotation(Operate.class);
		Object[] args = jp.getArgs();
		if( args.length > 0 && operate != null) {
			OperateEnum status = operate.status();
			System.out.println("keys:"+status);
			System.out.print("values:");
			for(int i=0;i<args.length;i++){
				System.out.print(args[i]);
			}
		}
		System.out.println();
	}
}

5、LoginService.java

代码语言:javascript
复制
package com.weeking.share.service.aop02.service;

import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import org.springframework.stereotype.Service;

@Service
public class LoginService {

    @Operate(status= OperateEnum.USERNAME)
    public String login(String username){

        System.out.println("login service username:"+username);
        return username;
    }
}

6、测试方法

代码语言:javascript
复制
@Test
    public void testAop02() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-02.xml");
        LoginService service = (LoginService)ctx.getBean(LoginService.class);
        service.login("jiang");
    }

7、运行结果

六、自动代理AOP03

1、dispatcherServlet-aop-03.xml

代码语言:javascript
复制
<?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-4.0.xsd 
               http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.weeking.share"/>

    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

2、AspectProxy03.java

代码语言:javascript
复制
package com.weeking.share.service.aop03;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 切面类,横切逻辑
 *
 */
@Component
@Aspect
public class AspectProxy03 {
    @Before("execution(* com.weeking.share.service.aop03.MathService.*(..))")
    public void before(JoinPoint jp){
        System.out.println("---------- AOP03 before execute ----------");
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();
        System.out.println("method : "+method.getName());
        Object[] args = jp.getArgs();
        if( args.length>0) {
            System.out.print("param:");
            for(int i=0;i<args.length;i++){
                System.out.print(args[i]+" ");
            }
        }
        System.out.println();
    }

    @After("execution(* com.weeking.share.service.aop03.MathService.*(..))")
    public void after(JoinPoint jp){
        System.out.println("---------- AOP03 after execute ----------");
    }
}

3、MathService.java

代码语言:javascript
复制
package com.weeking.share.service.aop03;

import org.springframework.stereotype.Service;

@Service
public class MathService {
    //加
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }

    //减
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }

    //乘
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }

    //除
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}

4、测试方法

代码语言:javascript
复制
@Test
public void testAop03() {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:dispatcherServlet-aop-03.xml");
    MathService math = ctx.getBean(MathService.class);
    int n1 = 100, n2 = 5;
    math.add(n1, n2);
    math.sub(n1, n2);
    math.mut(n1, n2);
    math.div(n1, n2);
}

5、运行结果

七、HTTP请求

1、LoginController.java

代码语言:javascript
复制
package com.weeking.share.controller;

import com.weeking.share.service.aop02.annotion.Operate;
import com.weeking.share.service.aop02.ennum.OperateEnum;
import com.weeking.share.service.aop02.service.LoginService;
import com.weeking.share.service.aop03.MathService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by jiangxingqi on 2017/7/25.
 */
@Controller
public class LoginController {

    @Autowired
    private LoginService loginService;
    @Autowired
    private MathService mathService;

    @RequestMapping(value = "/login")
    public String login(){
        String username=loginService.login("jiangxingqi");
        return "login";
    }
    @RequestMapping(value = "/math")
    public String math(){
        mathService.add(1,2);
        return "login";
    }
}

2、HTTP请求http://localhost:8084/login

控制台输出显示AOP成功执行

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年02月09日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、浅析AOP
  • 二、AOP项目预览
  • 三、基础配置
  • 四、 AOP方法01
  • 五、AOP02 注解实现
  • 六、自动代理AOP03
  • 七、HTTP请求
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档