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..0e9b73d79c2c34b74a53e588ea8e857018acb96a --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/aspectj/AspectJExpressionPointcutAdvisor.java @@ -0,0 +1,49 @@ +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; + } + + public void setExpression(String expression) { + this.expression = expression; + } +} 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..4f1680d8e33b28d8f0f434d3550c577be1b59c20 --- /dev/null +++ b/src/main/java/cn/noexception/container/aop/framework/adapter/MethodBeforeAdviceInterceptor.java @@ -0,0 +1,33 @@ +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(){} + + 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..5fe0b56b955a10ae2d74c8eda527300dc86978f5 --- /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 bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @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/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java b/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java index 8e7cd1b2a6cd232e8ccfaae8850bf3bca8d61986..8986aa40178813f7e809587921c8c6c81720f3c3 100644 --- a/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java +++ b/src/main/java/cn/noexception/container/factory/support/AbstractAutowiredCapableBeanFactory.java @@ -6,10 +6,7 @@ import cn.noexception.container.BeansException; import cn.noexception.container.PropertyValue; import cn.noexception.container.PropertyValues; import cn.noexception.container.factory.*; -import cn.noexception.container.factory.config.AutowireCapableBeanFactory; -import cn.noexception.container.factory.config.BeanDefinition; -import cn.noexception.container.factory.config.BeanPostProcessor; -import cn.noexception.container.factory.config.BeanReference; +import cn.noexception.container.factory.config.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -28,6 +25,12 @@ public abstract class AbstractAutowiredCapableBeanFactory extends AbstractBeanFa protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { Object bean = null; try { + // 判断是否返回代理Bean对象 + bean = resolveBeforeInstantiation(beanName, beanDefinition); + if (null != bean) { + return bean; + } + // 实例化 Bean bean = createBeanInstance(beanDefinition, beanName, args); // 给 Bean 填充属性值 applyPropertyValues(beanName, bean, beanDefinition); @@ -46,6 +49,24 @@ public abstract class AbstractAutowiredCapableBeanFactory extends AbstractBeanFa return bean; } + protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) { + Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName); + if (null != bean) { + bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); + } + return bean; + } + + protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { + for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName); + if (null != result) return result; + } + } + return null; + } + protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) { // 非 Singleton 类型的 Bean 不执行销毁方法 if (!beanDefinition.isSingleton()) return; diff --git a/src/test/java/cn/noexception/test/AopTest.java b/src/test/java/cn/noexception/test/AopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9ede2dfdc21f02b1260ad9ad644b84c017da357 --- /dev/null +++ b/src/test/java/cn/noexception/test/AopTest.java @@ -0,0 +1,104 @@ +package cn.noexception.test; + +import cn.noexception.container.aop.AdvisedSupport; +import cn.noexception.container.aop.ClassFilter; +import cn.noexception.container.aop.MethodMatcher; +import cn.noexception.container.aop.TargetSource; +import cn.noexception.container.aop.aspectj.AspectJExpressionPointcut; +import cn.noexception.container.aop.aspectj.AspectJExpressionPointcutAdvisor; +import cn.noexception.container.aop.framework.ProxyFactory; +import cn.noexception.container.aop.framework.ReflectiveMethodInvocation; +import cn.noexception.container.aop.framework.adapter.MethodBeforeAdviceInterceptor; +import cn.noexception.test.bean.IUserService; +import cn.noexception.test.bean.UserServiceBeforeAdvice; +import cn.noexception.test.bean.UserServiceInterceptor; +import cn.noexception.test.bean.impl.UserService; +import org.aopalliance.intercept.MethodInterceptor; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public class AopTest { + private AdvisedSupport advisedSupport; + + @Before + public void init() { + // 目标对象 + IUserService userService = new UserService(); + // 组装代理信息 + advisedSupport = new AdvisedSupport(); + advisedSupport.setTargetSource(new TargetSource(userService)); + advisedSupport.setMethodInterceptor(new UserServiceInterceptor()); + advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.noexception.test.bean.IUserService.*(..))")); + } + + @Test + public void test_proxyFactory() { + advisedSupport.setProxyTargetClass(false); + IUserService proxy = (IUserService) new ProxyFactory(advisedSupport).getProxy(); + + System.out.println("测试结果:" + proxy.queryUserInfo()); + } + + @Test + public void test_advisor() { + // 目标对象 + IUserService userService = new UserService(); + + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression("execution(* cn.noexception.test.bean.IUserService.*(..))"); + advisor.setAdvice(new MethodBeforeAdviceInterceptor(new UserServiceBeforeAdvice())); + + ClassFilter classFilter = advisor.getPointcut().getClassFilter(); + if (classFilter.matches(userService.getClass())) { + AdvisedSupport advisedSupport = new AdvisedSupport(); + + TargetSource targetSource = new TargetSource(userService); + advisedSupport.setTargetSource(targetSource); + advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); + advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); + advisedSupport.setProxyTargetClass(true); + + IUserService proxy = (IUserService) new ProxyFactory(advisedSupport).getProxy(); + System.out.println("测试结果:" + proxy.queryUserInfo()); + } + } + + @Test + public void test_proxy_method() { + // 目标对象(可替换成任何目标对象) + Object targetObj = new UserService(); + // AOP 代理 + IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() { + // 方法匹配器 + MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.noexception.test.bean.IUserService.*(..))"); + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (methodMatcher.matches(method, targetObj.getClass())) { + // 方法拦截器 + MethodInterceptor methodInterceptor = invocation -> { + long start = System.currentTimeMillis(); + try { + return invocation.proceed(); + } finally { + System.out.println("监控 - Begin By AOP"); + System.out.println("方法名称:" + invocation.getMethod().getName()); + System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms"); + System.out.println("监控 - End\r\n"); + } + }; + // 反射调用 + return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args)); + } + return method.invoke(targetObj, args); + } + }); + + String result = proxy.queryUserInfo(); + System.out.println("测试结果:" + result); + } +} 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..c9b2ae73c4533c5260548711dd0c439167a1d637 --- /dev/null +++ b/src/test/resources/config_aop.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file