Spring AOP

本文深入探讨了AOP(面向切面编程)的基本概念、实现原理及Spring AOP的具体应用。详细介绍了AspectJ的使用方法,包括声明切面、定义切入点、配置通知等关键步骤。

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

[b]AOP的实现原理[/b]
[list]
[*]编译期:切面在编译期织入,不过需要重新定义新的语法关键字并由特殊的编译器编译来实现 AspectJ
[*]类装载器:目标类被装载到Java虚拟机时,有一个特殊的类装载器对目标类的字节码进行增强
[*]运行期:目标对象和切面都是标准的Java类,通过Java虚拟机的动态代理功能或CGLib事项的运行期动态织入
[/list]


public class AopProxyFactory {

public static Object createProxy(final Object target,
final MethodBeforeAdvice methodBeforeAdvice) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
methodBeforeAdvice.before(method, args, target);
return method.invoke(target, args);
}
});
}
}


public class Main {
public static void main(String[] args) {
BookDao bookDao = new BookDao();
BookServiceImpl target = new BookServiceImpl();
target.setBookDao(bookDao);
MethodBeforeAdvice log = new MethodBeforeAdvice() {
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("call method" + arg0.getName());
}
};
BookService bookService = (BookService) AopProxyFactory.createProxy(target, log);
bookService.query();
}
}


术语解释
[list]
[*]Aspect:切面
切面是一个横跨多个核心逻辑的功能,或者称之为系统关注点,例如日志记录,事务处理,安全检查。以前我们在每个方法中调用重复编写这些代码,现在我们将这些散落的代码集中起来,放在一个模块中,这个模块就是切面
[*]Joinpoint:连接点
连接点就是应用程序流程的何处插入切面,(一个方法调用时,访问一个字段时,特定的异常抛出时)
[*]Pointcut:切入点
切入点是一组连接点的集合,比如一个切面要在调用,所有以delete开头的方法时执行,就可以用通配符delete*来表示这一组连接点,我们将其称为切面
[*]Advice:增强
在特定连接点上执行的动作,执行这个动作就相当于对原始对象的功能做了增强
[*]Introduction:引介
为一个已有的Java对象,动态的添加新的接口
[*]Weaving:织入
织入是将切面整合到程序中的过程
[*]Interceptor:拦截器
拦截器是一种实现增强的方式
[*]Target Object:目标对象
真正执行核心逻辑的对象。
[*]Advisor:一个Advice与Pointcut的结合
定义如何将advice织入目标对象
[/list]


常用Advice
1.MethodBeforeAdvice:在一个方法执行前增强
2.AfterReturningAdvice:在一个方法执行后增强
3.ThrowsAdvice:在一个方法执行前后增强
4.MethodInterceptor:在一个方法执行前后增强 可以控制整个方法执行与否,修改返回值,功能最强大。能使用上述三种简单的实现,就不要使用MethodInterceptor


public interface UserService {
void login(String username, String password);
boolean create(String username, String password);
}

@Service("userServiceTarget")
public class UserServiceImpl implements UserService {
public void login(String username, String password) {
if ("zhang".equals(username) && "zhang".equals(password)) {

} else {
throw new RuntimeException();
}
}
public boolean create(String username, String password) {
return "admin".equals(username) ? false : true;
}
}

@Component
public class LoginMethodBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("[LoginMethodBeforeAdvice] User " + args[0] + " try to login....");
}
}

@Component
public class LoginMethodAfterAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("[LoginMethodAfterAdvice] User " + args[0] + " is login...");
}
}

@Component
public class LoginMethodThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Throwable subClass) {
System.out.println("[LoginMethodThrowsAdvice] An exception occur: "
+ subClass.getClass().getSimpleName());
}
}

public class LoginMethodAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// 这里实现MethodBeforeAdvice的功能
Object ret = null;
try {
ret = invocation.proceed();
} catch (Exception e) {
// 这里实现ThrowsAdvice的功能
}
// 这里实现AfterReturningAdvice的功能
return ret;
}
}

public class Drive {
public static void main(String[] args) throws InterruptedException {

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService) context.getBean("userService");

try {
userService.login("zhang", "zhang");
Thread.sleep(500);
userService.login("xxxx", "invaild-password");
} catch (RuntimeException e) {
//System.err.println("invalid user...");
}
}
}

输出
[LoginMethodBeforeAdvice] User zhang try to login....
[LoginMethodAfterAdvice] User zhang is login...
[LoginMethodBeforeAdvice] User xxxx try to login....
[LoginMethodThrowsAdvice] An exception occur: RuntimeException


<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userServiceTarget"/>
<property name="interceptorNames">
<list>
<value>loginMethodBeforeAdvice</value>
<value>loginMethodAfterAdvice</value>
<value>loginMethodThrowsAdvice</value>
</list>
</property>
</bean>

三种Advice执行的位置不冲突,所以先后顺序无关,若加入MethodInterceptor,并且放置在最前面,则出现异常时,不执行invocation.proceed();其他三种Advice将不会被执行


[b]Advisor 控制只在某些方法上执行[/b]
1.NameMatchMethodPointcutAdvisor
2.RegexpMethodPointcutAdvisor

@Component
public class CreateAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("[CreateAfterReturningAdvice] create user " + args[0]);
}
}

public class Drive {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService) context.getBean("userService");

try {
userService.create("zhang", "zhang");
userService.login("zhang", "zhang");
Thread.sleep(500);
userService.create("admin", "invaild-password");
userService.login("admin", "invaild-password");
} catch (RuntimeException e) {
//System.err.println("invalid user...");
}
}
}

输出
[CreateAfterReturningAdvice] create user zhang
[LoginMethodAfterAdvice] User zhang is login...
[LoginMethodBeforeAdvice] User zhang try to login....
[LoginMethodAfterAdvice] User zhang is login...
[LoginMethodThrowsAdvice] An exception occur: RuntimeException



<bean id="createAfterReturningAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="createAfterReturningAdvice"/>
<property name="mappedName" value="create"/>
</bean>

<bean id="loginMethodBeforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="loginMethodBeforeAdvice"/>
<property name="pattern" value=".*login.*"/>
</bean>

<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userServiceTarget"/>
<property name="interceptorNames">
<list>
<value>loginMethodBeforeAdvisor</value>
<value>createAfterReturningAdvisor</value>
<!-- loginMethodAfterAdvice loginMethodThrowsAdvice 每个方法执行时都会被调用 -->
<value>loginMethodAfterAdvice</value>
<value>loginMethodThrowsAdvice</value>
</list>
</property>
</bean>


spring aop代理实际上包含了一个有Advisor和Advice组成的拦截链,Advice总是增强所有方法,Advisor同时包含Advice和Pointcut,因此Advisor会通过Pointcut计算是否应该调用Advice对某个方法进行增强。

[b]使用自动代理[/b]
1.自动为多个目标Bean实现Aop代理
2.避免客户端直接访问目标Bean
通过BeanPostProcessor实现,BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AspectJInvocationContextExposingAdvisorAutoProxyCreator AnnotationAwareAspectJAtuoProxyCreator


<!-- BeanNameAutoProxyCreator 所有以Service结尾的Bean -->
<bean id="beanNameAutoProxy"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" ref="*Service"/>
<property name="interceptorNames">
<list>
<value>loginMethodBeforeAdvisor</value>
<value>loginMethodAfterAdvice</value>
<value>loginMethodThrowsAdvice</value>
<value>createAfterReturningAdvisor</value>
</list>
</property>
</bean>

<!-- DefaultAdvisorAutoProxyCreator -->
<bean id="defaultAutoProxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />


[b]引介 Introduction[/b]
一种类型特殊的拦截器,不能作用于任何切入点,只能作用于类,而非方法级别,换句话说就是给类动态的添加接口

想让User类具有Mutable接口,通过isReadonly来判断是否可以更改User类的信息


public class User {

private String username;
private String password;
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

public interface Mutable {
boolean isReadonly();
void setReadonly(boolean isReadOnly);
}

public class MutableInterceptor extends DelegatingIntroductionInterceptor
implements Mutable {
private boolean readonly;
public boolean isReadonly() {
return this.readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
}


public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
User user = (User) context.getBean("user");
System.out.println(user.getEmail());
((Mutable)user).setReadonly(true);
System.out.println(((Mutable)user).isReadonly());
user.setEmail("abc@xyz.com");
System.out.println(user.getEmail());
}
输出
default-email
false
abc@xyz.com



<bean id="user" scope="prototype"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target"/>
<!-- 对类代理,因此设置为true,同时需要CGlib -->
<property name="proxyTargetClass" value="true"/>
<property name="proxyInterfaces">
<list>
<value>demo.aop.introduction.Mutable</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>introductionAdvisor</value>
</list>
</property>
</bean>

<bean id="introductionAdvisor" scope="prototype"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg>
<bean class="demo.aop.introduction.MutableInterceptor"/>
</constructor-arg>
</bean>

<bean id="target" class="demo.aop.introduction.User">
<property name="username" value="default-username"/>
<property name="password" value="default-password"/>
<property name="email" value="default-email"/>
</bean>

可以看到User对象却是具有了Mutable接口,因为没有ClassCastException。但是我们
ProxyFactoryBean返回的是一个AOP代理对象,因此它可以截获所有方法的调用,并委托给MethodInterceptor处理,MethodInterceptor集成自DelegatingIntroductionInterceptor,DelegatingIntroductionInterceptor实现了MethodInterceptor接口,因此可以实现方法拦截。

DelegatingIntroductionInterceptorde的invoke方法

public Object invoke(MethodInvocation mi) throws Throwable {
// 调用前是否是新的接口的方法?
if (isMethodOnIntroducedInterface(mi)) {
// 调用delegate对象的方法
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate,
mi.getMethod(), mi.getArguments());
if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
Object proxy = ((ProxyMethodInvocation) mi).getProxy();
if (mi.getMethod().getReturnType().isInstance(proxy)) {
retVal = proxy;
}
}
return retVal;
}
// 重写invoke方法,会拦截所有方法
// 如果只希望拦截原始对象(User)的方法,就覆写doProceed方法
return doProceed(mi);
}

// 修改后的 MutableInterceptor
public class MutableInterceptor extends DelegatingIntroductionInterceptor implements Mutable {
private boolean readonly;
public boolean isReadonly() {
return this.readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
@Override
protected Object doProceed(MethodInvocation mi) throws Throwable {
if (readonly && mi.getMethod().getName().startsWith("set")) {
throw new UnsupportedOperationException("Object is set to readonly");
}
return super.doProceed(mi);
}
}


禁止在运行期改变AOP代理
<property name="frozen" value="true"/>

[b]使用AspectJ实现AOP[/b]

// 1 声明Aspect
@Aspect
public class LoggerAspect {

}

// 2 声明Advice
@Before("execution(* demo.aop.impl.UserServiceImpl.login(..)) && args(username, ..)")
public void logBefore(String username){
System.out.println("[logger] User "+ username +" try to login....");
}

@Around("execution(* demo.aop.impl.UserServiceImpl.login(..))")
public Object securityCheck(ProceedingJoinPoint pjp) throws Throwable {
String username = (String) pjp.getArgs()[0];
if ("admin".equals(username)) {
System.out.println("[security check admin is forbidden]");
throw new RuntimeException();
}
return pjp.proceed();
}

// 3 开启AspectJ支持
<aop:aspectj-autoproxy/>

executionde的完整表达式:execution(修饰符? 返回类型 声明类型? 方法名称(参数类型) 异常类型?)

声明Pointcut
上面的Advice中,我们直接把Pointcut的表达式卸载相应的注解中。可以通过@Pointcut注解提取出来单独定义

@Aspect
@Component
public class LoggerAspect {

@Pointcut("execution(* demo.service.impl.UserServiceImpl.login(..))")
public void loginMethod(){}

@Before("loginMethod() && args(username, ..)")
public void logBefore(String username) {
System.out.println("[logger] User " + username + " try to login....");
}

@AfterReturning("loginMethod() && args(username, ..)")
public void logSuccess(String username) {
System.out.println("[logger] User " + username + " has login....");
}

@AfterThrowing(pointcut = "loginMethod()", throwing = "e")
public void logFailure(RuntimeException e) {
System.out.println("[logger] Exception: " + e.getMessage());
}

@Around("loginMethod()")
public Object securityCheck(ProceedingJoinPoint pjp) throws Throwable {
String username = (String) pjp.getArgs()[0];
if ("admin".equals(username)) {
System.out.println("[security check] admin is forbidden");
throw new RuntimeException();
}
return pjp.proceed();
}
}
内容概要:《中文大模型基准测评2025年上半年报告》由SuperCLUE团队发布,详细评估了2025年上半年中文大模型的发展状况。报告涵盖了大模型的关键进展、国内外大模型全景图及差距、专项测评基准介绍等。通过SuperCLUE基准,对45个国内外代表性大模型进行了六大任务(数学推理、科学推理、代码生成、智能体Agent、精确指令遵循、幻觉控制)的综合测评。结果显示,海外模型如o3、o4-mini(high)在推理任务上表现突出,而国内模型如Doubao-Seed-1.6-thinking-250715在智能体Agent和幻觉控制任务上表现出色。此外,报告还分析了模型性价比、效能区间分布,并对代表性模型如Doubao-Seed-1.6-thinking-250715、DeepSeek-R1-0528、GLM-4.5等进行了详细介绍。整体来看,国内大模型在特定任务上已接近国际顶尖水平,但在综合推理能力上仍有提升空间。 适用人群:对大模型技术感兴趣的科研人员、工程师、产品经理及投资者。 使用场景及目标:①了解2025年上半年中文大模型的发展现状与趋势;②评估国内外大模型在不同任务上的表现差异;③为技术选型和性能优化提供参考依据。 其他说明:报告提供了详细的测评方法、评分标准及结果分析,确保评估的科学性和公正性。此外,SuperCLUE团队还发布了多个专项测评基准,涵盖多模态、文本、推理等多个领域,为业界提供全面的测评服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值