Skip to content

Commit

Permalink
implements PointCut and AutoProxyCreator.
Browse files Browse the repository at this point in the history
  • Loading branch information
YaleGuo committed Jun 27, 2020
1 parent 4dafdb5 commit 86785a1
Show file tree
Hide file tree
Showing 25 changed files with 561 additions and 38 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ PooledConnection supported.

FactoryBean supported. Use JDKDynamicsProxy technology.

methodinterceptor,methodbeforeadvice, afterreturningadvice supported.
methodinterceptor,methodbeforeadvice, afterreturningadvice supported.

implements Pointcut and AutoProxyCreator.
125 changes: 124 additions & 1 deletion docs/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2117,4 +2117,127 @@ URL通过映射机制找到实际的业务逻辑方法。
<property type="java.lang.Object" name="target" ref="realaction"/>
<property type="String" name="interceptorName" value="myInterceptor"/>
</bean>



5.
到现在为止,我们invoke目标对象的时候,还是写死的method名,程序代码如下:
if (method.getName().equals("doAction")) {
}
我们需要改造这一点,通过某个给定的规则找到合适的method。

所以实现了pointcut,按照方法名匹配规则来匹配advice
<bean id="realaction" class="com.test.service.Action1" />
<bena id="beforeAdvice" class="com.test.service.MyBeforeAdvice" />

<bean id="advisor" class="com.minis.aop.NameMatchMethodPointcutAdvisor">
<property type="com.minis.aop.Advice" name="advice" ref="beforeAdvice"/>
<property type="String" name="mappedName" value="do*"/>
</bean>

<bean id="action" class="com.minis.aop.ProxyFactoryBean">
<property type="String" name="interceptorName" value="advisor" />
<property type="java.lang.Object" name="target" ref="realaction"/>
</bean>
配置文件中,advisor用了NameMatchMethodPointcutAdvisor,进行名称匹配。

类的定义:
public class NameMatchMethodPointcutAdvisor implements PointcutAdvisor{
private Advice advice = null;
private MethodInterceptor methodInterceptor;
private String mappedName;
private final NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
}
它里面除了advice和methodInterceptor之外,多了一个mappedName和pointcut。
如上面的配置例子, <property type="String" name="mappedName" value="do*"/>
匹配所有do开头的method。

匹配的工作交给NameMatchMethodPointcut来完成:
public class NameMatchMethodPointcut implements MethodMatcher,Pointcut{
private String mappedName = "";

public boolean matches(Method method, Class<?> targetClass) {
if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
return true;
}
return false;
}
protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
}
其实就是进行字符串的匹配判断。

有了这个匹配器,JdkDynamicAopProxy的invoke修改如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> targetClass = (target != null ? target.getClass() : null);

//if (method.getName().equals("doAction")) {
if (this.advisor.getPointcut().getMethodMatcher().matches(method, targetClass)) {
MethodInterceptor interceptor = this.advisor.getMethodInterceptor();
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass);

return interceptor.invoke(invocation);
}
return null;
}
以前直接判断method名字,现在改成:
this.advisor.getPointcut().getMethodMatcher().matches(method, targetClass))


6.
到现在为止,AOP基本实现了,不过有一个很大的问题,我们看配置文件就知道了:
<bean id="action" class="com.minis.aop.ProxyFactoryBean">
<property type="String" name="interceptorName" value="advisor" />
<property type="java.lang.Object" name="target" ref="realaction"/>
</bean>
我们的ProxyFactoryBean有一个target属性,上面的例子给的值是realaction。如果还有
第二个第三个目标对象,那么我们要相应地进行配置。一个规模软件,目标对象成百上千,这样的话,
配置太麻烦了。

所以我们来考虑自动生成代理,用beanpostprocessor实现。
将传进来的bean外面包装一个ProxyFactoryBean,改头换面塞进beanfactory。
public class BeanNameAutoProxyCreator implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (isMatch(beanName, this.pattern)) { //简单地根据bean name进行匹配
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(bean);
proxyFactoryBean.setBeanFactory(beanFactory);
proxyFactoryBean.setAopProxyFactory(aopProxyFactory);
proxyFactoryBean.setInterceptorName(interceptorName);
bean = proxyFactoryBean;
return proxyFactoryBean;
}
else {
return bean;
}
}
}

这样也要相应修改AbstractBeanFactory的getBean():
//beanpostprocessor
//step 1 : postProcessBeforeInitialization
singleton = applyBeanPostProcessorsBeforeInitialization(singleton, beanName);
//这一步将bean改头换面成proxyfactorybean

//step 2 : init-method
if (bd.getInitMethodName() != null && !bd.getInitMethodName().equals("")) {
invokeInitMethod(bd, singleton);
}

//step 3 : postProcessAfterInitialization
applyBeanPostProcessorsAfterInitialization(singleton, beanName);

this.removeSingleton(beanName);
this.registerBean(beanName, singleton);

配置文件既不需要逐个声明proxyfactorybean了,只要声明这个autoProxyCreator就可以了
<bean id="autoProxyCreator" class="com.minis.aop.framework.autoproxy.BeanNameAutoProxyCreator" >
<property type="String" name="pattern" value="action*" />
<property type="String" name="interceptorName" value="advisor" />
</bean>

利用这个机制,动态生成ProxyFactoryBean。
这样完整实现了AOP。

到此为止,我们实现了简单的IoC + MVC + JDBCTemplate + AOP
32 changes: 26 additions & 6 deletions resources/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,38 @@
</bean>
<bean id="baseservice" class="com.test.service.BaseService" />
<bean id="userService" class="com.test.service.UserService" />

<bean id="autoProxyCreator" class="com.minis.aop.framework.autoproxy.BeanNameAutoProxyCreator" >
<property type="String" name="pattern" value="action*" />
<property type="String" name="interceptorName" value="advisor" />
</bean>
<bean id="autowiredAnnotationBeanPostProcessor" class="com.minis.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="logBeanPostProcessor" class="com.test.LogBeanPostProcessor" />

<bean id="myInterceptor" class="com.test.service.MyInterceptor" />

<bean id="realaction" class="com.test.service.Action1" />

<!-- <bean id="realaction" class="com.test.service.Action1" />
<bean id="action" class="com.minis.aop.ProxyFactoryBean" >
<property type="java.lang.Object" name="target" ref="realaction"/>
<property type="String" name="interceptorName" value="myInterceptor"/>
</bean>
</bean> -->

<bean id="action" class="com.test.service.Action1" />
<bean id="action2" class="com.test.service.Action2" />

<bena id="beforeAdvice" class="com.test.service.MyBeforeAdvice" />
<bean id="advisor" class="com.minis.aop.NameMatchMethodPointcutAdvisor">
<property type="com.minis.aop.Advice" name="advice" ref="beforeAdvice"/>
<property type="String" name="mappedName" value="do*"/>
</bean>



<!-- <bean id="action" class="com.minis.aop.ProxyFactoryBean">
<property type="String" name="interceptorName" value="advisor" />
<property type="java.lang.Object" name="target" ref="realaction"/>
</bean>
-->


<!-- <bean id="dataSource" class="com.minis.jdbc.datasource.SingleConnectionDataSource">
<property type="String" name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
Expand All @@ -48,8 +70,6 @@

<bean id="beanFactoryPostProcessor" class="com.test.MyBeanFactoryPostProcessor" />

<bean id="autowiredAnnotationBeanPostProcessor" class="com.minis.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="logBeanPostProcessor" class="com.test.LogBeanPostProcessor" />


<bean id="handlerMapping" class="com.minis.web.method.annotation.RequestMappingHandlerMapping"/>
Expand Down
1 change: 1 addition & 0 deletions src/com/minis/aop/Advisor.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public interface Advisor {
MethodInterceptor getMethodInterceptor();
void setMethodInterceptor(MethodInterceptor methodInterceptor);
Advice getAdvice();
}
2 changes: 1 addition & 1 deletion src/com/minis/aop/AopProxyFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.minis.aop;

public interface AopProxyFactory {
AopProxy createAopProxy(Object target,Advisor adviseor);
AopProxy createAopProxy(Object target, PointcutAdvisor adviseor);
}
4 changes: 4 additions & 0 deletions src/com/minis/aop/DefaultAdvisor.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ public MethodInterceptor getMethodInterceptor() {
return this.methodInterceptor;
}

@Override
public Advice getAdvice() {
return this.methodInterceptor;
}
}
2 changes: 1 addition & 1 deletion src/com/minis/aop/DefaultAopProxyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public class DefaultAopProxyFactory implements AopProxyFactory {

@Override
public AopProxy createAopProxy(Object target, Advisor advisor) {
public AopProxy createAopProxy(Object target, PointcutAdvisor advisor) {
return new JdkDynamicAopProxy(target, advisor);
}
}
9 changes: 5 additions & 4 deletions src/com/minis/aop/JdkDynamicAopProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
Object target;
Advisor advisor;
PointcutAdvisor advisor;

public JdkDynamicAopProxy(Object target, Advisor advisor) {
public JdkDynamicAopProxy(Object target, PointcutAdvisor advisor) {
this.target = target;
this.advisor = advisor;
}
Expand All @@ -21,9 +21,10 @@ public Object getProxy() {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("doAction")) {
Class<?> targetClass = (target != null ? target.getClass() : null);

Class<?> targetClass = (target != null ? target.getClass() : null);
if (this.advisor.getPointcut().getMethodMatcher().matches(method, targetClass)) {
//if (method.getName().equals("doAction")) {
MethodInterceptor interceptor = this.advisor.getMethodInterceptor();
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass);
Expand Down
7 changes: 7 additions & 0 deletions src/com/minis/aop/MethodMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.minis.aop;

import java.lang.reflect.Method;

public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
}
30 changes: 30 additions & 0 deletions src/com/minis/aop/NameMatchMethodPointcut.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.minis.aop;

import java.lang.reflect.Method;
import com.minis.util.PatternMatchUtils;

public class NameMatchMethodPointcut implements MethodMatcher,Pointcut{
private String mappedName = "";

public void setMappedName(String mappedName) {
this.mappedName = mappedName;
}

@Override
public boolean matches(Method method, Class<?> targetClass) {
if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
return true;
}
return false;
}

protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}

@Override
public MethodMatcher getMethodMatcher() {
return this;
}

}
58 changes: 58 additions & 0 deletions src/com/minis/aop/NameMatchMethodPointcutAdvisor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.minis.aop;

public class NameMatchMethodPointcutAdvisor implements PointcutAdvisor{
private Advice advice = null;
private MethodInterceptor methodInterceptor;
private String mappedName;
private final NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();

public NameMatchMethodPointcutAdvisor() {
}

public NameMatchMethodPointcutAdvisor(Advice advice) {
this.advice = advice;
}

public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}

public MethodInterceptor getMethodInterceptor() {
return this.methodInterceptor;
}


public void setAdvice(Advice advice) {
this.advice = advice;

MethodInterceptor mi = null;

if (advice instanceof BeforeAdvice) {
mi = new MethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);
}
else if (advice instanceof AfterAdvice){
mi = new AfterReturningAdviceInterceptor((AfterReturningAdvice)advice);
}
else if (advice instanceof MethodInterceptor) {
mi = (MethodInterceptor)advice;
}

setMethodInterceptor(mi);
}

@Override
public Advice getAdvice() {
return this.advice;
}

@Override
public Pointcut getPointcut() {
return pointcut;
}

public void setMappedName(String mappedName) {
this.mappedName = mappedName;
this.pointcut.setMappedName(this.mappedName);
}

}
8 changes: 8 additions & 0 deletions src/com/minis/aop/Pointcut.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.minis.aop;

public interface Pointcut {
//ClassFilter getClassFilter();

MethodMatcher getMethodMatcher();

}
5 changes: 5 additions & 0 deletions src/com/minis/aop/PointcutAdvisor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.minis.aop;

public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
Loading

0 comments on commit 86785a1

Please sign in to comment.