diff --git a/src/main/java/cn/noexception/container/aop/AdvisedSupport.java b/src/main/java/cn/noexception/container/aop/AdvisedSupport.java index 21438851eadb8e3123d7f0ab964860a07154e9fe..0f3941cbb29b5509eb3f949343f3371277fc4e28 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 0000000000000000000000000000000000000000..77b4cfbdb4a0ba8d7ac8592801c4b9ce7ecd0b9e --- /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 0000000000000000000000000000000000000000..5ecf2734c7e070f0a9ac271c92495e1c5457907d --- /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 0000000000000000000000000000000000000000..3d3deaf19eeb47e6677fb6f3d87b090fb0e9cf7e --- /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 0000000000000000000000000000000000000000..f38e79903a57538d150ca4c6653ce85dff5a5fa3 --- /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 0000000000000000000000000000000000000000..385e87455728c30c3d4bb5a6d32fdd07c45a5301 --- /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 0000000000000000000000000000000000000000..26ef244cc7247731a8818bfb3d8c84180c30ee3d --- /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 0000000000000000000000000000000000000000..059cf23612ea6cfc35e47168fd36cdef9e8f967d --- /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 0000000000000000000000000000000000000000..5a4310b4c04daa9d8d6a115e862e059b05d4e5ba --- /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 0000000000000000000000000000000000000000..e2c93ef12e3c2c1801ffc2786b8a72b85159bc28 --- /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 626f28185c1d3a7e002fe012fdf0808f9088de94..25821f7c1c7bfb3c40750a193e224a9aa641672a 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 0000000000000000000000000000000000000000..8b5413e3c660e63007d9b2f64bd89ca14d34a066 --- /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 0000000000000000000000000000000000000000..fb3e866fd8da3edc60bad3cc3643e71ed716f9eb --- /dev/null +++ b/src/test/resources/config_aop.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file