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按需拦截
+ */
+ 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 @@
+
+