Spring Aop基础: 动态代理

本文深入解析Spring AOP的基本概念,包括Aspect、Joinpoint、Advice等,并通过基于注解和XML配置的示例,展示如何在实际项目中运用AOP进行日志记录、异常处理等跨切关注点的编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重要概念:

1、Aspect

This is a module which has a set of APIs providing cross-cutting requirements. For example, a logging module would be called AOP aspect for logging. An application can have any number of aspects depending on the requirement.

2、Join point

This represents a point in your application where you can plug-in the AOP aspect. You can also say, it is the actual place in the application where an action will be taken using Spring AOP framework.

3、Advice

This is the actual action to be taken either before or after the method execution. This is an actual piece of code that is invoked during the program execution by Spring AOP framework.

4、Pointcut

This is a set of one or more join points where an advice should be executed. You can specify pointcuts using expressions or patterns as we will see in our AOP examples.

5、Introduction

An introduction allows you to add new methods or attributes to the existing classes.

6、Target object

The object being advised by one or more aspects. This object will always be a proxied object, also referred to as the advised object.

7、Weaving

Weaving is the process of linking aspects with other application types or objects to create an advised object. This can be done at compile time, load time, or at runtime.

通知类型(Types of Advice)

Spring aspects can work with five kinds of advice mentioned as follows −

before

Run advice before the a method execution.

after

Run advice after the method execution, regardless of its outcome.

after-returning

Run advice after the a method execution only if method completes successfully.

after-throwing

Run advice after the a method execution only if method exits by throwing an exception.

around

Run advice before and after the advised method is invoked.

织入(wave)

是把切面应用到目标对象并创建新的代理对象的过程。 切面在指定的连接点被织入到目标对象中。

看图说话

examples

1. based Annotation

// Loggin.java file 

package com.dft;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;

@Component
@Aspect
public class Logging {
   /** Following is the definition for a pointcut to select
      *  all the methods available. So advice will be called
      *  for all the methods.
   */
   @Pointcut("execution(* com.dft.*.*(..))")
   private void selectAll(){}

   /** 
      * This is the method which I would like to execute
      * before a selected method execution.
   */
   @Before("selectAll()")
   public void beforeAdvice(){
      System.out.println("Going to setup student profile.");
   }

   /** 
      * This is the method which I would like to execute
      * after a selected method execution.
   */
   @After("selectAll()")
   public void afterAdvice(){
      System.out.println("Student profile has been setup.");
   }

   /** 
      * This is the method which I would like to execute
      * when any method returns.
   */
   @AfterReturning(pointcut = "selectAll()", returning = "retVal")
   public void afterReturningAdvice(Object retVal){
      System.out.println("Returning:" + retVal.toString() );
   }

   /**
      * This is the method which I would like to execute
      * if there is an exception raised by any method.
   */
   @AfterThrowing(pointcut = "selectAll()", throwing = "ex")
   public void AfterThrowingAdvice(IllegalArgumentException ex){
      System.out.println("There has been an exception: " + ex.toString());   
   }
}


//Student.java file 

package com.dft;

@Component
public class Student {
   private Integer age;
   private String name;

   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
	  System.out.println("Age : " + age );
      return age;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      System.out.println("Name : " + name );
      return name;
   }
   public void printThrowException(){
      System.out.println("Exception raised");
      throw new IllegalArgumentException();
   }
}

//MainApp.java file
package com.dft;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      
      Student student = (Student) context.getBean("student");
      student.getName();
      student.getAge();
      
      student.printThrowException();
   }
}


/*
    output messages : 
    
    Going to setup student profile.
    Name : Zara
    Student profile has been setup.
    Returning:Zara
    Going to setup student profile.
    Age : 11
    Student profile has been setup.
    Returning:11
    Going to setup student profile.
    Exception raised
    Student profile has been setup.
    There has been an exception: java.lang.IllegalArgumentException
    .....
    other exception content
**/

2. based xml

// Logging.java file. This is actually a sample of aspect module which defines the methods to be called at various points.

package com.dft;
public class Logging {
   /** 
      * This is the method which I would like to execute
      * before a selected method execution.
   */
   public void beforeAdvice(){
      System.out.println("Going to setup student profile.");
   }
   
   /** 
      * This is the method which I would like to execute
      * after a selected method execution.
   */
   public void afterAdvice(){
      System.out.println("Student profile has been setup.");
   }

   /** 
      * This is the method which I would like to execute
      * when any method returns.
   */
   public void afterReturningAdvice(Object retVal) {
      System.out.println("Returning:" + retVal.toString() );
   }

   /**
      * This is the method which I would like to execute
      * if there is an exception raised.
   */
   public void AfterThrowingAdvice(IllegalArgumentException ex){
      System.out.println("There has been an exception: " + ex.toString());   
   }
}

// Student.java file

package com.dft;
public class Student {
   private Integer age;
   private String name;

   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
	  System.out.println("Age : " + age );
      return age;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      System.out.println("Name : " + name );
      return name;
   }
   public void printThrowException(){
	   System.out.println("Exception raised");
      throw new IllegalArgumentException();
   }
}


//  MainApp.java file

package com.dft;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
      
      Student student = (Student) context.getBean("student");
      student.getName();
      student.getAge();
      student.printThrowException();
   }
}

// Beans.xml file
<?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-3.0.xsd 
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

   <aop:config>
      <aop:aspect id = "log" ref = "logging">
         <aop:pointcut id = "selectAll" 
            expression = "execution(* com.dft.*.*(..))"/>
         
         <aop:before pointcut-ref = "selectAll" method = "beforeAdvice"/>
         <aop:after pointcut-ref = "selectAll" method = "afterAdvice"/>
         <aop:after-returning pointcut-ref = "selectAll" 
            returning = "retVal" method = "afterReturningAdvice"/>
         
         <aop:after-throwing pointcut-ref = "selectAll" 
            throwing = "ex" method = "AfterThrowingAdvice"/>
      </aop:aspect>
   </aop:config>

   <!-- Definition for student bean -->
   <bean id = "student" class = "com.dft.Student">
      <property name = "name" value = "Zara" />
      <property name = "age" value = "11"/>      
   </bean>

   <!-- Definition for logging aspect -->
   <bean id = "logging" class = "com.dfd.Logging"/> 
      
</beans>

/*
    output messages : 
    
    Going to setup student profile.
    Name : Zara
    Student profile has been setup.
    Returning:Zara
    Going to setup student profile.
    Age : 11
    Student profile has been setup.
    Returning:11
    Going to setup student profile.
    Exception raised
    Student profile has been setup.
    There has been an exception: java.lang.IllegalArgumentException
    .....
    other exception content
**/


Understanding Dynamic Proxy : Spring AOP Basics

Proxy Pattern:

We can create a proxy of the object , which will take care of the cross cutting concern code.There are two kind of proxy patterns :

  • Static Proxy :

Where we create a proxy object for every class. This is not feasible and practical

  • Dynamic Proxy :

In this , proxies are created dynamically through reflection . This functionality is added from JDK 1.3 . dynamic Proxy form the basic building block of Spring AOP

package com.somaniab.blog.ex;

public interface Basicfunc{

 public void method1();       
 
}
package com.somaniab.blog.ex;

public class Example1 implements Basicfunc{

 @Override
 public void method1() {
  System.out.println("executing method 1");  
 }
     
}

Now if we want to calculate execution time of method1 , we have to write that code in method itself , or we can create a proxy object. For creating proxy object , we create a Invocationhandler as following code.

In this invocation handler , we call the actual method as well as calculate the time taken.Now , in main class we create the proxy object by using Proxy class.

package com.somaniab.blog.ex;

import java.lang.reflect.Proxy;

public class MainClass {
      public static void main(String[] args) {
  
       Example1 ex = new Example1();
       
       Basicfunc proxied =(Basicfunc)Proxy
                           .newProxyInstance(MainClass.class.getClassLoader(),
         ex.getClass().getInterfaces() ,new InvocationHandler() {

					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("proxy executing");
						long t1 = System.currentTimeMillis();
						method.invoke(ex, args);
						System.out.println(System.currentTimeMillis() - t1);
						System.out.println("proxy executed");
						return null;
					}
				});
       proxied.method1();
 }
}

For creating Proxy we pass classloader[mostly the same classloader as origin class],interfaces,and the invocationHandler (pass the original target object in the invocation handler ). The original class must implement a interface ,only those method declared in interface get proxied and then we cast the proxy to the interface type .

In CGLib Proxy , there is no necessity of declaring interface .
So , this is how we made sure that our Example class write code for only method1 and we kept execution time calculation code out of it.

参考1
参考2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值