From bbce07761d4c493eba46b61afe2487a72bd0871d Mon Sep 17 00:00:00 2001 From: lance Date: Thu, 4 Nov 2021 18:04:19 +0800 Subject: [PATCH] =?UTF-8?q?:=20=E5=B0=86AOP=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E9=9B=86=E6=88=90=E8=BF=9B=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=AE=B9=E5=99=A8=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../container/aop/AdvisedSupport.java | 11 +++ .../cn/noexception/container/aop/Advisor.java | 17 ++++ .../container/aop/BeforeAdvice.java | 12 +++ .../container/aop/MethodBeforeAdvice.java | 16 ++++ .../container/aop/PointcutAdvisor.java | 11 +++ .../AspectJExpressionPointcutAdvisor.java | 45 +++++++++++ .../container/aop/framework/ProxyFactory.java | 30 +++++++ .../MethodBeforeAdviceInterceptor.java | 31 +++++++ .../DefaultAdvisorAutoProxyCreator.java | 81 +++++++++++++++++++ .../InstantiationAwareBeanPostProcessor.java | 16 ++++ .../java/cn/noexception/test/ApiRunner.java | 8 ++ .../test/bean/UserServiceBeforeAdvice.java | 18 +++++ src/test/resources/config_aop.xml | 17 ++++ 13 files changed, 313 insertions(+) create mode 100644 src/main/java/cn/noexception/container/aop/Advisor.java create mode 100644 src/main/java/cn/noexception/container/aop/BeforeAdvice.java create mode 100644 src/main/java/cn/noexception/container/aop/MethodBeforeAdvice.java create mode 100644 src/main/java/cn/noexception/container/aop/PointcutAdvisor.java create mode 100644 src/main/java/cn/noexception/container/aop/aspectj/AspectJExpressionPointcutAdvisor.java create mode 100644 src/main/java/cn/noexception/container/aop/framework/ProxyFactory.java create mode 100644 src/main/java/cn/noexception/container/aop/framework/adapter/MethodBeforeAdviceInterceptor.java create mode 100644 src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java create mode 100644 src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java create mode 100644 src/test/java/cn/noexception/test/bean/UserServiceBeforeAdvice.java create mode 100644 src/test/resources/config_aop.xml diff --git a/src/main/java/cn/noexception/container/aop/AdvisedSupport.java b/src/main/java/cn/noexception/container/aop/AdvisedSupport.java index 2143885..0f3941c 100644 --- a/src/main/java/cn/noexception/container/aop/AdvisedSupport.java +++ b/src/main/java/cn/noexception/container/aop/AdvisedSupport.java @@ -9,6 +9,9 @@ import org.aopalliance.intercept.MethodInterceptor; * @Date 2021/11/3 14:46 */ public class AdvisedSupport { + + private boolean proxyTargetClass = false; + // 被代理的目标对象 private TargetSource targetSource; // 方法拦截器 @@ -16,6 +19,14 @@ public class AdvisedSupport { // 方法匹配器(检查目标方法是否符合通知条件) private MethodMatcher methodMatcher; + public boolean isProxyTargetClass() { + return proxyTargetClass; + } + + public void setProxyTargetClass(boolean proxyTargetClass) { + this.proxyTargetClass = proxyTargetClass; + } + public TargetSource getTargetSource() { return targetSource; } diff --git a/src/main/java/cn/noexception/container/aop/Advisor.java b/src/main/java/cn/noexception/container/aop/Advisor.java new file mode 100644 index 0000000..77b4cfb --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/Advisor.java @@ -0,0 +1,17 @@ +package cn.noexception.container.aop; + +import org.aopalliance.aop.Advice; + +/** + * Advisor + * 定义访问者 + *

+ * Advisor 承担了 Pointcut 和 Advice 的组合, + * Pointcut 用于获取 JoinPoint,而 Advice 决定于 JointPoint 执行什么操作 + *

+ * @author 吕滔 + * @Date 2021/11/4 16:17 + */ +public interface Advisor { + Advice getAdvice(); +} diff --git a/src/main/java/cn/noexception/container/aop/BeforeAdvice.java b/src/main/java/cn/noexception/container/aop/BeforeAdvice.java new file mode 100644 index 0000000..5ecf273 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/BeforeAdvice.java @@ -0,0 +1,12 @@ +package cn.noexception.container.aop; + +import org.aopalliance.aop.Advice; + +/** + * BeforeAdvice + * + * @author 吕滔 + * @Date 2021/11/4 15:18 + */ +public interface BeforeAdvice extends Advice { +} diff --git a/src/main/java/cn/noexception/container/aop/MethodBeforeAdvice.java b/src/main/java/cn/noexception/container/aop/MethodBeforeAdvice.java new file mode 100644 index 0000000..3d3deaf --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/MethodBeforeAdvice.java @@ -0,0 +1,16 @@ +package cn.noexception.container.aop; + +import java.lang.reflect.Method; + +/** + * MethodBeforeAdvice + * + * @author 吕滔 + * @Date 2021/11/4 16:07 + */ +public interface MethodBeforeAdvice extends BeforeAdvice{ + /** + * 给定方法调用之前的回调 + */ + void before(Method method, Object[] args, Object target) throws Throwable; +} diff --git a/src/main/java/cn/noexception/container/aop/PointcutAdvisor.java b/src/main/java/cn/noexception/container/aop/PointcutAdvisor.java new file mode 100644 index 0000000..f38e799 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/PointcutAdvisor.java @@ -0,0 +1,11 @@ +package cn.noexception.container.aop; + +/** + * PointcutAdvisor + * + * @author 吕滔 + * @Date 2021/11/4 16:27 + */ +public interface PointcutAdvisor extends Advisor { + Pointcut getPointcut(); +} diff --git a/src/main/java/cn/noexception/container/aop/aspectj/AspectJExpressionPointcutAdvisor.java b/src/main/java/cn/noexception/container/aop/aspectj/AspectJExpressionPointcutAdvisor.java new file mode 100644 index 0000000..385e874 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/aspectj/AspectJExpressionPointcutAdvisor.java @@ -0,0 +1,45 @@ +package cn.noexception.container.aop.aspectj; + +import cn.noexception.container.aop.Pointcut; +import cn.noexception.container.aop.PointcutAdvisor; +import org.aopalliance.aop.Advice; + +/** + * AspectJExpressionPointcutAdvisor + *

+ * 实现了 PointcutAdvisor 接口,把切面、拦截方法和具体拦截表达式包装在一起。
+ * 这样就可以在 xml 的配置中定义一个 pointcutAdvisor 切面拦截器了。 + *

+ * + * @author 吕滔 + * @Date 2021/11/4 16:30 + */ +public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor { + // 切面 + private AspectJExpressionPointcut pointcut; + // 具体的拦截方法 + private Advice advice; + // 表达式 + private String expression; + + public String getExpression() { + return expression; + } + + public void setAdvice(Advice advice) { + this.advice = advice; + } + + @Override + public Advice getAdvice() { + return advice; + } + + @Override + public Pointcut getPointcut() { + if (null == pointcut) { + pointcut = new AspectJExpressionPointcut(expression); + } + return pointcut; + } +} diff --git a/src/main/java/cn/noexception/container/aop/framework/ProxyFactory.java b/src/main/java/cn/noexception/container/aop/framework/ProxyFactory.java new file mode 100644 index 0000000..26ef244 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/framework/ProxyFactory.java @@ -0,0 +1,30 @@ +package cn.noexception.container.aop.framework; + +import cn.noexception.container.aop.AdvisedSupport; + +/** + * ProxyFactory

+ * - 代理工厂类,用于解决关于选择Cglib 和 JDK 两种代理的问题

+ * - 有了代理工厂就可以安札不同的创建需求进行控制 + * + * @author 吕滔 + * @Date 2021/11/4 16:43 + */ +public class ProxyFactory { + private AdvisedSupport advisedSupport; + + public ProxyFactory(AdvisedSupport advisedSupport) { + this.advisedSupport = advisedSupport; + } + + public Object getProxy() { + return createAopProxy().getProxy(); + } + + private AopProxy createAopProxy() { + if (advisedSupport.isProxyTargetClass()) { + return new Cglib2AopProxy(advisedSupport); + } + return new JdkDynamicAopProxy(advisedSupport); + } +} diff --git a/src/main/java/cn/noexception/container/aop/framework/adapter/MethodBeforeAdviceInterceptor.java b/src/main/java/cn/noexception/container/aop/framework/adapter/MethodBeforeAdviceInterceptor.java new file mode 100644 index 0000000..059cf23 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/framework/adapter/MethodBeforeAdviceInterceptor.java @@ -0,0 +1,31 @@ +package cn.noexception.container.aop.framework.adapter; + +import cn.noexception.container.aop.MethodBeforeAdvice; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * MethodBeforeAdviceInterceptor + * - 方法拦截器 + * @author 吕滔 + * @Date 2021/11/4 16:34 + */ +public class MethodBeforeAdviceInterceptor implements MethodInterceptor { + private MethodBeforeAdvice advice; + + public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { + this.advice = advice; + } + + /** + * 实现了 MethodInterceptor 接口,调用 advice 中的 before 方法,传入对应的参数信息。 + * @param invocation + * @return + * @throws Throwable + */ + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + this.advice.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis()); + return invocation.proceed(); + } +} diff --git a/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java new file mode 100644 index 0000000..5a4310b --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java @@ -0,0 +1,81 @@ +package cn.noexception.container.aop.framework.autoproxy; + +import cn.noexception.container.BeansException; +import cn.noexception.container.aop.*; +import cn.noexception.container.aop.aspectj.AspectJExpressionPointcutAdvisor; +import cn.noexception.container.aop.framework.ProxyFactory; +import cn.noexception.container.factory.BeanFactory; +import cn.noexception.container.factory.BeanFactoryAware; +import cn.noexception.container.factory.config.InstantiationAwareBeanPostProcessor; +import cn.noexception.container.factory.support.DefaultListableBeanFactory; +import org.aopalliance.aop.Advice; +import org.aopalliance.intercept.MethodInterceptor; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +/** + * DefaultAdvisorAutoProxyCreator + *

加入 Bean 生命周期的自动代理创建者

+ * + * @author 吕滔 + * @Date 2021/11/4 16:53 + */ +public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { + + private DefaultListableBeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (DefaultListableBeanFactory) beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + if (isInfrastructureClass(beanClass)) return null; + Collection advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values(); + + for (AspectJExpressionPointcutAdvisor advisor : advisors) { + ClassFilter classFilter = advisor.getPointcut().getClassFilter(); + if (!classFilter.matches(beanClass)) + continue; + + AdvisedSupport advisedSupport = new AdvisedSupport(); + TargetSource targetSource = null; + try { + targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + e.printStackTrace(); + } + // 设置目标对象 + advisedSupport.setTargetSource(targetSource); + // 设置拦截方法 + advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); + // 设置匹配器 + advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); + // 设置选择使用的代理方法 + advisedSupport.setProxyTargetClass(false); + + return new ProxyFactory(advisedSupport).getProxy(); + } + + return null; + } + + /** + * 检测/感知 bean 是否是切点

按需拦截 + */ + private boolean isInfrastructureClass(Class beanClass) { + return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass); + } +} diff --git a/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java b/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java new file mode 100644 index 0000000..e2c93ef --- /dev/null +++ b/src/main/java/cn/noexception/container/factory/config/InstantiationAwareBeanPostProcessor.java @@ -0,0 +1,16 @@ +package cn.noexception.container.factory.config; + +import cn.noexception.container.BeansException; + +/** + * InstantiationAwareBeanPostProcessor + * + * @author 吕滔 + * @Date 2021/11/4 16:55 + */ +public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { + /** + * 在 Bean 对象执行初始化方法之前,执行此方法 + */ + Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException; +} diff --git a/src/test/java/cn/noexception/test/ApiRunner.java b/src/test/java/cn/noexception/test/ApiRunner.java index 626f281..25821f7 100644 --- a/src/test/java/cn/noexception/test/ApiRunner.java +++ b/src/test/java/cn/noexception/test/ApiRunner.java @@ -71,4 +71,12 @@ public class ApiRunner { // 测试调用 System.out.println("测试结果:"+proxy_cglib.register("感冒灵")); } + + @Test + public void test_aop(){ + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:config_aop.xml"); + IUserService userService = applicationContext.getBean("userService", IUserService.class); + + System.out.println("测试结果:"+userService.queryUserInfo()); + } } diff --git a/src/test/java/cn/noexception/test/bean/UserServiceBeforeAdvice.java b/src/test/java/cn/noexception/test/bean/UserServiceBeforeAdvice.java new file mode 100644 index 0000000..8b5413e --- /dev/null +++ b/src/test/java/cn/noexception/test/bean/UserServiceBeforeAdvice.java @@ -0,0 +1,18 @@ +package cn.noexception.test.bean; + +import cn.noexception.container.aop.MethodBeforeAdvice; + +import java.lang.reflect.Method; + +/** + * UserServiceBeforeAdvice + * + * @author 吕滔 + * @Date 2021/11/4 17:30 + */ +public class UserServiceBeforeAdvice implements MethodBeforeAdvice { + @Override + public void before(Method method, Object[] args, Object target) throws Throwable { + System.out.println("拦截方法:" + method.getName()); + } +} diff --git a/src/test/resources/config_aop.xml b/src/test/resources/config_aop.xml new file mode 100644 index 0000000..fb3e866 --- /dev/null +++ b/src/test/resources/config_aop.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file -- GitLab