Spring AOP-----------基础

Posted 没昵称可用

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP-----------基础相关的知识,希望对你有一定的参考价值。

AOP概述

  1、AOP术语

    连接点:连接点好比一个类的方法,每一个方法都是一个连接点

    切点:每个方法有大量的逻辑构成,可以将任何一个位置作为执行点,这个执行点作为切点。

    增强:就是嵌入方法中的一段逻辑。

    目标对象:织入增强的目标类。

    引介:特殊的增强,为类加入方法和属性。

    织入:将增强嵌入切点的过程。

    代理:织入增强后产生的结果类。

    切面:切入点+增强

创建增强类

  前置增强

    前置增强=连接点前+增强,增强只是针对所有的的连接点。前置增强的实现类为MethodBeforeAdvice

    前置增强的过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过MethodBeforeAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。

    实例:      

    创建目标类

package advice;

public interface Waiter {
	void greetTo(String name);
	void serverTo(String name);
}

  

package advice;

public class NaiveWaiter implements Waiter {

	@Override
	public void greetTo(String name) {
		// TODO Auto-generated method stub
		System.out.println("greet to"+name+"......");
	}

	@Override
	public void serverTo(String name) {
		// TODO Auto-generated method stub
		System.out.println("serving "+name+"......");
	}

}

    创建前置增强

package advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		// TODO Auto-generated method stub
		//arg0标识目标方法,arg1标识目标方法的参数值,arg2标识目标实例
		System.out.println("====="+arg0.getName()+"=====");
		String clientName = (String) arg1[0];
		System.out.println("How are you!"+clientName+".");
		System.out.println("===="+arg2.getClass().getName()+"====");
	}

}

   设置前置增强

package advice;

import org.springframework.aop.framework.ProxyFactory;

public class TestBeforeAdvice {

	public static void main(String[] args){
		//创建目标实例
		NaiveWaiter waiter = new NaiveWaiter();
		//创建增强实例
		GreetingBeforeAdvice advice = new GreetingBeforeAdvice();
		//创建代理工厂
		ProxyFactory factory = new ProxyFactory();
		//设置代理目标
		factory.setTarget(waiter);
		//为代理类添加增强
		factory.addAdvice(advice);
		//生成代理实例
		Waiter proxy = (Waiter) factory.getProxy();
		proxy.greetTo("JSON");
		proxy.serverTo("JSON");
	}
}

  Spring配置前置增强实例:

    application配置

<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:util="http://www.springframework.org/schema/util"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context-3.0.xsd
                    http://www.springframework.org/schema/mvc
                    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util-3.0.xsd
                    http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="beforeadvice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:proxyInterfaces="beforeadvice.Waiter"
	p:interceptorNames="greetingBeforeAdvice"
	p:target-ref="naiveWaiter"
/>
</beans>

    设置前置增强

package beforeadvice;

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

public class TestSpringBefore {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter = (Waiter) ap.getBean("waiter");
		waiter.greetTo("JACK");
		waiter.serverTo("JACK");
	}
}

  后置增强

    后置增强=连接点后+增强,后置增强是将在方法后织入增强,后置增强的实现类为AfterReturningAdvice

    前置增强的过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过AfterReturningAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。

    实例:

     创建后置增强类

package advice.after;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class GreetingAfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("please enjoy yourself!");
	}

}

    设置后置增强

package advice.after;

import org.springframework.aop.framework.ProxyFactory;

import com.sun.org.glassfish.external.statistics.Statistic;

import advice.before.GreetingBeforeAdvice;
import advice.before.NaiveWaiter;
import advice.before.Waiter;

public class TestAfterAdvice {

	public static void main(String[] args){
		//创建目标实例
		NaiveWaiter waiter = new NaiveWaiter();
		//创建增强实例
		GreetingAfterAdvice advice = new GreetingAfterAdvice();
		//创建代理工厂
		ProxyFactory factory = new ProxyFactory();
		//设置代理目标
		factory.setTarget(waiter);
		//为代理类添加增强
		factory.addAdvice(advice);
		//生成代理实例
		Waiter proxy = (Waiter) factory.getProxy();
		proxy.greetTo("JSON");
		proxy.serverTo("JSON");
	}
}

   Spring配置后置增强实例:

    application配置 

<?xml version="1.0" encoding="UTF-8"?>
<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:util="http://www.springframework.org/schema/util"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context-3.0.xsd
                    http://www.springframework.org/schema/mvc
                    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util-3.0.xsd
                    http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="advice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:proxyInterfaces="advice.before.Waiter"
	p:target-ref="naiveWaiter"
>
	<property name="interceptorNames">
		<list>
			<value>greetingBeforeAdvice</value>
			<value>after</value>
		</list>
	</property>
</bean>

</beans>

    设置后置增强

package advice.before;

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

public class TestSpringBefore {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter = (Waiter) ap.getBean("waiter");
		waiter.greetTo("JACK");
		waiter.serverTo("JACK");
	}
}

 环绕增强

    环绕增强=连接点前后+增强,在方法的前后都会织入增强,环绕增强的实现类为MethodInterceptor。

    环绕增强过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过AfterReturningAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。 

    Spring配置环绕增强实例:

    定义增强类

package advice.interceptor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
@Component("interceptor")
public class GreetingInterceptor implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		// TODO Auto-generated method stub
		//目标方法入参
		Object[] args = arg0.getArguments();
		String str = (String) args[0];
		System.out.println(str+"先生,你好!");
		//执行目标方法
		Object object = arg0.proceed();
		System.out.println(str+"先生,有什么帮忙的?");
		return object;
	}

}

    设置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:p="http://www.springframework.org/schema/p"
 xmlns:util="http://www.springframework.org/schema/util"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context-3.0.xsd
                    http://www.springframework.org/schema/mvc
                    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util-3.0.xsd
                    http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="advice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:proxyInterfaces="advice.before.Waiter"
	p:target-ref="naiveWaiter"
>
	<property name="interceptorNames">
		<list>
			<value>greetingBeforeAdvice</value>
			<value>after</value>
			<value>interceptor</value>
		</list>
	</property>
</bean>

</beans>

    设置环绕增强

package advice.before;

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

public class TestSpringBefore {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter = (Waiter) ap.getBean("waiter");
		waiter.greetTo("JACK");
		waiter.serverTo("JACK");
	}
}

  异常抛出增强   

    异常抛出增强=连接点异常后+增强,在异常后会织入增强,异常抛出增强的实现类为ThrowsAdvice。

    异常抛出增强过程:1、创建代理工厂;2、创建目标类,也就是需要织入增强的类;3、通过ThrowsAdvice生成增强类;4、将增强类和目标类添加到代理工厂;5、生成目标代理。

    Spring配置异常增强实例:

     创建目标类

package advice.throwsadvice;

import java.sql.SQLException;

import org.springframework.stereotype.Component;
@Component
public class ForumService {

	public void removeForum(){
		throw new RuntimeException("运行异常.........");
	}
	
	public void updateForum() throws SQLException{
		throw new SQLException("数据库更新异常..........");
	}
}

    创建异常增强类

package advice.throwsadvice;

import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.concurrent.Executor;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
@Component
public class TransactionThrows implements ThrowsAdvice{

	public void afterThrowing(Method method,Object[] objects, 
			Object target, RuntimeException exception){
		System.out.println("操作报错========"+"RuntimeException");
	}
	
	public void afterThrowing(Method method,Object[] objects, 
			Object target, SQLException exception){
		System.out.println("操作报错========"+"SQLException");
	}
}

  Spring配置异常增强

<bean id="forum" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:target-ref="forumService">
	<property name="interceptorNames">
		<value>
		transactionThrows
		</value>
	</property>
</bean>

  引介增强

   引介增强只是对目标类添加属性和方法。

创建切面

    增强只是对每个类中的所有方法都要织入,没有条件限制。切面可以定位到目标类和目标方法进行增强的织入。

   切点类型

      • 静态方法切点:StaticMethodMatcherPointcut类过滤接入点的类和方法。
      • 动态方法切点:DynamicMethodMatcherPointcut类在每次运行程序时都要过滤接入点的类和方法。
      • 注解切点
      • 表达式切点
      • 流程切点
      • 复合切点

   切面类型

      • Advisor:代表一般切面。
      • PoincutAdvisor:代表具有切入点的切面。
      • IntroductionAdvisor:代表引介切面。

   PointcutAdvisor

    PointcutAdvisor共有6个具体实现类

    • DefaultPointcutAdvisor:主要通过Pointcut和Advice构成一个切面,例如动态切面、流程切面、复合切面等切面的构成。
    • StaticMethodMatcherPointcutAdvisor:用于设置静态切面
    • NameMethodMatcherPointcutAdvisor:按照方法名定义切点的切面。
    • RegexMethodMatcherPointcutAdvisor:通过正则表达式匹配方法名定义切点的切面。
    • AspectJExpressionPointcutAdvisor
    • AspectJPointcutAdvisor

    1、静态普通方法名匹配切面

    该切面会通过类名和方法名对增强进行准确的织入。StaticMethodMatcherPointcutAdvisor方法对目标类和方法进行过滤。

    创建过程:1、创建目标类和方法;2、通过StaticMethodMatcherPointcutAdvisor创建切面对接入点进行过滤;3、创建增强;4、将增强注入切面;5、将目标类、切面加入代理。

    Waiter.java文件

package advisor;

import org.springframework.stereotype.Component;

@Component("waiterTarget")
public class Waiter {

	public void greetTo(String name){
		System.out.println("waiter greet to "+name+"......");
	}
	
	public void serveTo(String name){
		System.out.println("waiter serving to "+name+".....");
	}
}

    Seller.java文件

package advisor;

import org.springframework.stereotype.Component;

@Component("sellerTarget")
public class Seller {

	public void greetTo(String name){
		System.out.println("seller greet to "+name+"......");
	}
	
	public void serveTo(String name){
		System.out.println("seller serving to "+name+".....");
	}
}

  创建增强类

package advice.before;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
@Component
public class GreetingBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		// TODO Auto-generated method stub
		//arg0标识目标方法,arg1标识目标方法的参数值,arg2标识目标实例
		System.out.println("====="+arg0.getName()+"=====");
		String clientName = (String) arg1[0];
		System.out.println("How are you!"+clientName+".");
		System.out.println("===="+arg2.getClass().getName()+"====");
	}

}

  创建切面

package advisor.staticmethod;

import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;

import advisor.Waiter;

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor{

	@Override
	public boolean matches(Method method, Class<?> arg1) {
		// TODO Auto-generated method stub
		System.out.println("开始过滤方法=======");
		return "greetTo".equals(method.getName());
	}
	@Override
	public ClassFilter getClassFilter(){
		
		return new ClassFilter() {
			public boolean matches(Class clazz) {
				// TODO Auto-generated method stub
				System.out.println("开始过滤类=======");
				return Waiter.class.isAssignableFrom(clazz);
			}
		};
	}

}

  Spring配置文件  

<!-- 向切面注入前置增强 -->
<bean id="greetingAdvisor" class="advisor.staticmethod.GreetingAdvisor"
	p:advice-ref="greetingBeforeAdvice"/>
<!-- 定义公共配置信息 -->
<bean id="parent"  abstract="true"
	class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="greetingAdvisor"
	p:proxyTargetClass="true"/>
<bean id="waiter1" parent="parent" p:target-ref="waiterTarget"/>
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>

  测试

package advisor;

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

public class TestAdvisor {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter =  (Waiter) ap.getBean("waiter1");
		waiter.greetTo("李小龙");
	}
}

    2、静态正则表达式方法匹配切面 

    该切面会通过类名和方法名对增强进行准确的织入。RegexMethodMatcherPointcutAdvisor方法对目标类和方法进行过滤。

    创建过程:1、创建目标类和方法;2、通过RegexMethodMatcherPointcutAdvisor创建切面对接入点进行过滤;3、创建增强;4、将目标类、切面和增强加入代理。

    3、动态切面

     动态切点在每次执行时都要进行目标类和目标方法的检查,虽然这会对服务性能造成影响,因此在进行动态检查时,首先进行静态检查,检查通过后进行动态检查。动态切面使用的类为DynamicMethodMatcherPointcut

    创建过程:1、创建目标类和方法;2、通过DynamicMethodMatcherPointcut创建动态切面;3、创建增强;4、将增强和动态切面注入默认切面;5、将目标类、切面注入代理。

    创建动态切点类  

package advisor.dynamic;


import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;

import advisor.Waiter;

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut{

	private static List<String> list = new ArrayList<String>();
	
	static{
		list.add("李小刚");
	}
	public boolean matches(Method method, java.lang.Class<?> targetClass) {
		System.out.println("对方法进行静态过滤=========");
		return "serveTo".equals(method.getName());
	}
	@Override
	public boolean matches(Method arg0, Class<?> arg1, Object... arg2) {
		// TODO Auto-generated method stub
		System.out.println("对方法进行动态过滤=================");
		String clientname = (String) arg2[0];
		return list.contains(clientname);
	}
	
	public ClassFilter getClassFilter(){
		System.out.println("对类进行过滤===============");
		return new ClassFilter() {
			
			@Override
			public boolean matches(Class<?> arg0) {
				// TODO Auto-generated method stub
				return Waiter.class.isAssignableFrom(arg0);
			}
		};
	}

}

  Spring进行配置动态切面

<bean id="parent1"  abstract="true"
	class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="dynamicAdvisor"
	p:proxyTargetClass="true"/>
<bean id="waiter2" parent="parent1" p:target-ref="waiterTarget"/>

  测试

package advisor;

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

public class TestAdvisor {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter =  (Waiter) ap.getBean("waiter2");
		waiter.serveTo("李小刚");
		
	}
}

    4、流程切面

    流程切面与动态切面相似,但是效率较低。流程切面主要有DefaultPointcutAdvisor和ControlFlowPointcut实现。

    创建过程:1、创建目标类和方法;2、创建增强;3、ControlFolwPointcut类注入目标类和方法生成切点;4、将切点和增强注入普通切面;5、将切面和目标方法注入代理;

    Spring文件配置

<!-- 设置流程切点 -->
<bean id="contrlflowpointcut" class="org.springframework.aop.support.CrontrolFolwPointcut">
	<constructor-arg type="java.lang.Class" value=""/>
	<constructor-arg type="java.lang.String" value="service"/>
</bean>
<!-- 设置流程切面 -->
<bean id="controlflowadvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="pointcut">
		<ref bean="contrlflowpointcut"/>
	</property>
	<property name="advice">
		<bean class="advice.before.GreetingBeforeAdvice"/>
	</property>
</bean>
<!-- 设置目标代理类 -->
<bean id="parent2"  abstract="true"
	class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="controlflowadvisor"
	p:proxyTargetClass="true"/>
<bean id="waiter3" parent="parent2" p:target-ref="waiterTarget"/>

  需要织入增强的类

package advisor.controlfolw;

import advisor.Waiter;

public class WaiterDelegate {

	private Waiter waiter;

	public Waiter getWaiter() {
		return waiter;
	}

	public void setWaiter(Waiter waiter) {
		this.waiter = waiter;
	}
	
	public void service(String clientName){
		waiter.greetTo(clientName);
		waiter.serveTo(clientName);
	}
}

  测试

package advisor;

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

import advisor.controlfolw.WaiterDelegate;

public class TestAdvisor {

	public static void main(String[] args){
		String path = "application.xml";
		ApplicationContext ap = new ClassPathXmlApplicationContext(path);
		Waiter waiter =  (Waiter) ap.getBean("waiter3");
		WaiterDelegate delegate = new WaiterDelegate();
		delegate.setWaiter(waiter);
		delegate.service("小刚");
		
	}
}

    5、复合切点切面

     复合切点切面是将多个切点添加到复合切点对象中,生成复合切点的类为ComposablePointcut类。复合切点可以由两种方法实现,1、insertsection方法实现切点交集;2、union方法实现切点并集。

    创建过程:1、通过ComposablePointcut创建复合切点,将其他类型的切点加入其中。2、将复合切点和增强注入切面Bean中;3、将切面注入代理Bean。

    创建复合切点类

    

package advisor.composable;

import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.stereotype.Component;

import advisor.controlfolw.WaiterDelegate;
@Component("componsable")
public class GreetingComposablePointcut {

	public ComposablePointcut getComposablePointcut(){
		ComposablePointcut pointcut = new ComposablePointcut();
		Pointcut pc = new ControlFlowPointcut(WaiterDelegate.class, "service");
		NameMatchMethodPointcut mPointcut = new NameMatchMethodPointcut();
		mPointcut.addMethodName("greetTo");
		return pointcut.intersection(pc).intersection(mPointcut);
	}
}

  Spring配置

<!-- 创建复合切面 -->
<bean id="composableadvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="pointcut">
		<ref bean="#componsable.composablePointcut"/>
	</property>
	<property name="advice">
		<bean class="advice.before.GreetingBeforeAdvice"/>
	</property>
</bean>
<!-- 创建目标代理 -->
<bean id="parent3"  abstract="true"
	class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="composableadvisor"
	p:proxyTargetClass="true"/>
<bean id="waiter4" parent="parent3" p:target-ref="waiterTarget"/>

 

以上是关于Spring AOP-----------基础的主要内容,如果未能解决你的问题,请参考以下文章

Spring基础之AOP

Spring基础系列Spring AOP基础

Spring AOP基础实践

Web基础之Spring AOP与事务

(spring-第17回AOP基础篇) 创建增强类

Spring基础:AOP概念和原理