赞
踩
环境:Spring6.1.2
1. 简介
在Spring框架中,AOP(面向切面编程)是一种强大的编程范式,它允许开发者在不修改原有代码的情况下,为程序添加额外的功能,如日志记录、事务管理、安全控制等。
实际开发中常用实现AOP配置方式:
在早期的Spring版本中,开发者常常使用XML配置文件来定义切面、通知和目标对象之间的关联。通过配置[aop:config](aop:config)
、[aop:aspect](aop:aspect)
、[aop:before](aop:before)
等标签,可以轻松地实现AOP的各种功能。如下示例:
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.pack.service.*.*(..))"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
</aop:aspect>
</aop:config>
通过在切面类和方法上使用如@Aspect
、@Before
、@After
等注解,可以更加简洁地定义AOP的相关配置。这种方式不仅减少了XML配置的工作量,还使得代码更加清晰易读。如下示例:
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* save(..))")
private void logPc() {}
@Around("logPc()")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
Object ret = null ;
System.out.println("before log...") ;
ret = pjp.proceed() ;
System.out.println("after log...") ;
return ret ;
}
}
以上是Spring提供的2中方式来声明AOP配置方式。但如果你需要一种更加灵活和可配置性,那么Spring还提供了一个非常方便强大的ProxyFactoryBean类,该类特别适合那些需要更多自定义和控制的场景,例如当你需要为特定的Bean创建代理,或者需要在不修改原始代码的情况下为现有类添加额外的功能时。
2. 实战案例
ProxyFactoryBean与其他Spring FactoryBean实现一样,引入了一个间接级别。如果定义了名为pack的ProxyFactoryBean,那么引用pack的对象看不到ProxyFactoryBean实例本身,而是由ProxyFactoryBean#getObject() 方法实现创建的对象。此方法创建一个AOP代理,用于包装目标对象。
2.1 属性配置
ProxyFactoryBean提供了很多属性,让你可以灵活的配置代理对象。该对象继承了ProxyConfig,一些关键的属性是由ProxyConfig定义。
接下来将从2方面介绍ProxyFactoryBean的使用,代理接口与代理类。
2.2 代理接口
要通过ProxyFactoryBean创建代理,你至少需要涉及到下面几点(类):
如下示例:
public interface ICommonDAO { void save() ; } @Component("commonDAOTarget") public class CommonDAOImpl implements ICommonDAO { @Override public void save() { System.out.println("save operator...") ; } } @Component public class LogInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before log...") ; Object ret = invocation.proceed() ; System.out.println("after log...") ; return ret ; } } @Configuration public class AppConfig { @Bean // 由于上面已经定义了CommonDAOImpl,而这里的FactoryBean#getObject返回的 // 也是一个实现了ICommonDAO接口的对象,所以需要加上@Primary @Primary ProxyFactoryBean commonDAO(@Qualifier("commonDAOTarget") CommonDAOImpl commonDAOTarget) throws Exception { ProxyFactoryBean proxy = new ProxyFactoryBean() ; proxy.setProxyInterfaces(new Class<?>[] {ICommonDAO.class}) ; proxy.setTarget(commonDAOTarget) ; proxy.setInterceptorNames("logInterceptor") ; return proxy ; } }
测试
ICommonDAO dao = context.getBean(ICommonDAO.class) ;
dao.save() ;
// 输出
before log...
save operator...
after log...
2.3 代理类
如果我们的目标没有实现接口,那么我们只能通过CGLIB进行代理,通过设置proxyTargetClass属性为true。CGLIB代理通过在运行时生成目标类的子类来工作。Spring将这个生成的子类配置为将方法调用委托给原始目标。如下示例:
@Component("commonDAOTarget") public class CommonDAO { public void save() { System.out.println("save operator...") ; } } @Bean @Primary ProxyFactoryBean commonDAO(@Qualifier("commonDAOTarget") CommonDAO commonDAOTarget) throws Exception { ProxyFactoryBean proxy = new ProxyFactoryBean() ; proxy.setTarget(commonDAOTarget) ; proxy.setInterceptorNames("logInterceptor") ; // 代理类,可以不设置 proxy.setProxyTargetClass(true) ; return proxy ; }
查看最终的CommonDAO是否是通过CGLIB代理
CommonDAO dao = context.getBean(CommonDAO.class) ;
System.out.println(dao.getClass()) ;
输出结果
class com.pack.aop.create.ProxyFactoryBeanTest2$CommonDAO$$SpringCGLIB$$1
CGLIB代理通过在运行时生成目标类的子类来工作。但需要注意以下事项:
2.4 模糊匹配拦截器
在上面配置拦截器时,我们都是指定的具体拦截器,其实我们还可以使用通配符,指定拦截器。如下示例:
@Component("global_log")
public class LogInterceptor implements MethodInterceptor {
}
@Component("global_auth")
public class AuthInterceptor implements MethodInterceptor {
}
// ProxyFactoryBena配置
ProxyFactoryBean commonDAO() throws Exception {
ProxyFactoryBean proxy = new ProxyFactoryBean() ;
// 注意:这里的通配符必须是最后,你不能放到其它位置
proxy.setInterceptorNames("global_*") ;
return proxy ;
}
以上ProxyFactoryBean在初始化时,会自动查找容器中beanName以global_ 开头的所有Bean对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。