环境:Spring6.1.2
在Spring框架中,AOP(面向切面编程)是一种强大的编程范式,它允许开发者在不修改原有代码的情况下,为程序添加额外的功能,如日志记录、事务管理、安全控制等。
实际开发中常用实现AOP配置方式:
在早期的Spring版本中,开发者常常使用XML配置文件来定义切面、通知和目标对象之间的关联。通过配置<aop:config>、<aop:aspect>、<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@Aspectpublic 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创建代理,或者需要在不修改原始代码的情况下为现有类添加额外的功能时。
ProxyFactoryBean与其他Spring FactoryBean实现一样,引入了一个间接级别。如果定义了名为pack的ProxyFactoryBean,那么引用pack的对象看不到ProxyFactoryBean实例本身,而是由ProxyFactoryBean#getObject()方法实现创建的对象。此方法创建一个AOP代理,用于包装目标对象。
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...") ; }}@Componentpublic 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 ; }}@Configurationpublic 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...
如果我们的目标没有实现接口,那么我们只能通过CGLIB进行代理,通过设置proxyTargetClass属性为true。CGLIB代理通过在运行时生成目标类的子类来工作。Spring将这个生成的子类配置为将方法调用委托给原始目标。如下示例:
@Component("commonDAOTarget")public class CommonDAO { public void save() { System.out.println("save operator...") ; }}@Bean@PrimaryProxyFactoryBean 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代理通过在运行时生成目标类的子类来工作。但需要注意以下事项:
在上面配置拦截器时,我们都是指定的具体拦截器,其实我们还可以使用通配符,指定拦截器。如下示例:
@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对象。
本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-86683-0.htmlSpring一个强大便捷的代理工厂类,你用过吗?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: HTTP 协议是怎么来的?最开始是什么样子?又是如何一步步发展 HTTP3
下一篇: 一种避免写大量CRUD方法的新思路