一、需求:
自己实现AOP:1.0版本:在某个方法上加”@InOutLog”注解,那么执行到该方法时,方法的前面、后面会输出日志信息。
【自己实现AOP 2.0版本(实现Spring的有前置通知、后置通知、返回通知等各种通知的AOP):https://www.cnblogs.com/laipimei/p/11163377.html】
二、思路整理:
1.涉及的角色:
①被代理类;
②被代理类要实现的接口;
③代理类;
④动态创建“代理类的对象”的类;
⑤注解类(注解在方法上);
⑥IOC容器:BeanFactory(自己实现IOC容器:https://www.cnblogs.com/laipimei/p/11205510.html)。
2.实现步骤:
(1)被代理类、被代理类的接口、InOutLog注解类的创建;
(2)创建一个“动态代理类”,并把“被代理类的实例”传给该代理类;在该动态代理类的invoke()方法中,实现日志的输出,也是在该invoke()方法中调用、执行真正的代理类要执行的那个方法。
(3)创建一个可以动态创建“代理类的实例”的类,通过该类的getProxyInstance(Object obj)方法可以得到一个动态代理类的实例。
(4)给方法加通知注解,该方法的实例须已交由IOC容器管理的;
(5)遍历BeanFactory,找出方法上有@InOutLog注解的bean,为这些bean生成代理类对象(步骤:MyProxy3.getProxyInstance(Object obj));
(6)用代理类的实例去替代BeanFactory中的被代理类的实例;
三、代码实现:
被代理类的接口:
package MyIOCAndMyAop.bean;
public interface Subject { void test(); }
被代理类:
1 package MyIOCAndMyAop.bean;
2
3 import MyIOCAndMyAop.Annotations.InOutLog; 4 import MyIOCAndMyAop.Annotations.MyAutowired; 5 import MyIOCAndMyAop.Annotations.MyComponent; 6 7 /** 8 * 被代理类 9 */ 10 @MyComponent 11 public class Person implements Subject{ 12 13 @MyAutowired 14 private Student student; 15 16 @InOutLog 17 public void test(){ 18 System.out.println(this + ".test() : " + student); 19 } 20 }
InOutLog注解类:
package MyIOCAndMyAop.Annotations;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface InOutLog { }
动态代理类:
public class MyInvocationHandler2 implements InvocationHandler{
private Object object;//被代理类
private Object invoke; public void setObject(Object object) { this.object = object; } /** * 在BeanFactory中,方法上有@InOutLog注解,则生成动态代理方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //这里做判断,看是否需要做下面的输出 Boolean bool = false; //!!!注意,要用被代理类的对象去判断其method方法上是否有@InOutLog注解,而不是用入参method对象(该method对象是被代理类的接口的) //怎么处理入参的类型:见MyAOP2.这里没有做入参处理,可能会报方法找不到异常,注意!!! Method declaredMethod = object.getClass().getDeclaredMethod(method.getName()); if(null != declaredMethod.getAnnotation(InOutLog.class)) { System.out.println("我是日志输出~~~start~~~"); bool = true; } invoke = method.invoke(object, args); //这里做判断,同上 if(bool) { System.out.println("我是日志输出~~~end~~~"); System.out.println("------------------------------------------------------------------------------"); } return invoke; } }
动态创建“代理类的对象”的类:
public class MyProxy2 {
/**
* 动态的创建一个代理类的对象
* MyProxy动态创建的“代理类的对象”:
* class A implements Subject{
* private Handler handler;
* public void test() {
* //获得到当前方法名
* handler.invoke();
* }
* }
*/
public static Object getProxyInstance(Object obj) { MyInvocationHandler2 handler = new MyInvocationHandler2(); handler.setObject(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } /** * 对于有@InOutLog注解的,用代理类的bean来替代BeanFactory中的被代理类的bean。 * 这一步很重要,因为当执行到bean.method(),执行的就一定是bean对应的method()方法, * 如果此时没有用代理类对象去替换,那么执行的就是没有InOutLog的原来的那个方法。 */ public static void updateBean(String completeClassName, Object object) { MyIOC.updateBeanFromBeanFactory(completeClassName, getProxyInstance(object));//(全类名,代理类的bean) } }
①扫描BeanFactory,找出方法上有@InOutLog注解的bean,为其创建代理类对象,并替代原bean。②使用测试:
public class MyAOP2 {
public static void main(String[] args) { /** * 使用步骤: * ① 給指定类的某个方法加@InOutLog注解; * ② 通过BeanFactory或的该类的实例; * ③ 执行bean.method(); * 效果:method()方法的前后有了log的输出。 */ String completeClassName = "MyIOCAndMyAop.bean.Person"; Object bean = MyIOC.getBean(completeClassName); Subject person = (Subject)bean; person.test(); } static { init(); } public static void init() { updateBeanFromBeanFactory(); } /** * 扫描BeanFactory,找出方法上有@InOutLog注解的bean,为其创建代理类对象,并替代原bean。 */ public static void updateBeanFromBeanFactory() { for(Map.Entry<String, Object> entry : MyIOC.getBeanFactory().entrySet()) { for(Method method : entry.getValue().getClass().getDeclaredMethods()) { if(null != method.getAnnotation(InOutLog.class)) { MyProxy2.updateBean(entry.getKey(), entry.getValue()); } } } } }