AOP是一种面向切面编程的技术,要实现AOP就不得不提到Proxy模式,具体代理模式的例子可以参考 设计模式整理
由于代理模式都必须有一个统一的接口,我们先来写一个接口
public interface Greeting { void sayHello(String name); }
还有一个实现类,是实现了它的主要功能。
public class GreetingImpl implements Greeting { public void sayHello(String name) { System.out.println("Hello! " + name); } }
再写一个代理类来代理它(在设计模式的例子中,我们是在需要的时候,干重活的时候才创建实现类的实例,此处为直接创建)
public class GreetingProxy implements Greeting { //代理类对实现类的委托 private GreetingImpl greetingImpl; public GreetingProxy(GreetingImpl greetingImpl) { this.greetingImpl = greetingImpl; } public void sayHello(String name) { befor(); greetingImpl.sayHello(name); after(); } private void befor() { System.out.println("Before"); } private void after() { System.out.println("after"); } }
通过main方法对它具体调用
public class Client { public static void main(String[] args) { Greeting greetingProxy = new GreetingProxy(new GreetingImpl()); greetingProxy.sayHello("Jack"); } }
运行结果
Before
Hello! Jack
after
这样我们就对这一个实现类进行了环绕,但如果这样的实现类非常多怎么办呢?这个时候我们就需要用到动态代理了。
public class DynamicProxy<T> implements InvocationHandler { private Object target; public DynamicProxy(Object target) { this.target = target; } //获取目标对象的代理类实例 public T getProxy() { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), //目标对象类的加载器 target.getClass().getInterfaces(), //目标对象类的接口数组(因为一个类可能有几个接口) this ); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(target,args); after(); return result; } private void before() { System.out.println("Before"); } private void after() { System.out.println("After"); } }
main方法
public class Client { public static void main(String[] args) { Greeting greetingProxy = new DynamicProxy<Greeting>(new GreetingImpl()).getProxy(); greetingProxy.sayHello("Jack"); } }
运行结果与之前的代理模式相同
Before
Hello! Jack
After
动态代理是通过反射来实现的,这里有一个Proxy.newProxyInstance,我们来看一下它的源代码(带注释)
//代理类的构造器的参数类型数组 private static final Class<?>[] constructorParams = { InvocationHandler.class };
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //要求动态代理类本身不为空,如果为空会抛出异常 Objects.requireNonNull(h); //将接口数组克隆出来 final Class<?>[] intfs = interfaces.clone(); //启动Java安全管理器 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //检查调用类是否有启动接口的Class实例的加载器的权限 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. * 创建代理类的Class实例 */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { //检查调用类是否有该代理类的权限 checkNewProxyPermission(Reflection.getCallerClass(), cl); } //通过构造参数类型来获取代理类的具体某一个构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); //代理类本身实例 final InvocationHandler ih = h; //获取代理类Class实例的所有修饰符,并判断是否为public,如果不为public则通过cons.setAccessible(true)设置为可以访问 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //通过该具体的构造器来构造代理类本身的实例,之前我们有说过Class的newInstance只能通过无参构造器来构造,但此处是选择了具体的有参构造器构造 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
由以上代码可知,该动态代理必须要有接口才能代理,而不能代理没有接口的类。
没有接口的类,我们可以使用CGLib来动态代理
要使用CGLib,先要在pom中加入它的引用
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
public class CGLibDynamicProxy<T> implements MethodInterceptor { private static CGLibDynamicProxy instance = new CGLibDynamicProxy(); private CGLibDynamicProxy() { } /** * 通过单例模式获取代理类的实例 * @return */ public static CGLibDynamicProxy getInstance() { return instance; } /** * 通过实现类的Class实例跟代理类对象关联 * @param cls * @return */ public T getProxy(Class<T> cls) { return (T)Enhancer.create(cls,this); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object result = methodProxy.invokeSuper(o,objects); after(); return result; } private void before() { System.out.println("Before"); } private void after() { System.out.println("After"); } }
main方法
public class Client { public static void main(String[] args) { Greeting greetingProxy = (Greeting) CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class); greetingProxy.sayHello("Jack"); } }
运行结果与之前一样,我们来简单看一下Enhancer.create的源码
public static Object create(Class type, Callback callback) { Enhancer e = new Enhancer(); //创建一个增强代理类对象,它可以拦截被代理的除final,static以外的所有类 e.setSuperclass(type); //设置该代理类的父类为实现类 e.setCallback(callback); //设置实现了MethodInterceptor接口的回调类,这里即为我们动态代理类本身的实例,因为MethodInterceptor是继承于Callback的接口,它是一个 //对象方法拦截器 return e.create(); //返回实现类的增强类(所谓增强即可以对这个类进行拦截和各种处理操作) }
了解了动态代理之后,我们来了解一下Spring+AspectJ的AOP。我们主要结合拦截指定注解(@annotation)的方式来大概说明一下用法。
比如说我们要拦截一个自定义的标签,对标有该标签的方法,记录日志,通过MQ(具体不限定哪种MQ)发送到日志中心,进行存储。
首先我们有一个日志的实体类
@Builder @Data @NoArgsConstructor @AllArgsConstructor public class Log implements Serializable { private static final long serialVersionUID = -5398795297842978376L; private Long id; private String username; /** 模块 */ private String module; /** 参数值 */ private String params; private String remark; private Boolean flag; private Date createTime; private String ip; private String area; }
有一个自定义的标签,这是一个方法级标签
/** * 日志注解 * */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface LogAnnotation { String module(); /** * 记录参数<br> * 尽量记录普通参数类型的方法,和能序列化的对象 * * @return */ boolean recordParam() default true; }
增加日志所属的模块(用户操作)
/** * 日志模块定义 * */ public abstract class LogModule { public static final Map<String, String> MODULES = new HashMap<>(); public static final String LOGIN = "LOGIN"; public static final String LOGOUT = "LOGOUT"; public static final String ADD_PERMISSION = "ADD_PERMISSION"; public static final String UPDATE_PERMISSION = "UPDATE_PERMISSION"; public static final String DELETE_PERMISSION = "DELETE_PERMISSION"; public static final String ADD_ROLE = "ADD_ROLE"; public static final String UPDATE_ROLE = "UPDATE_ROLE"; public static final String DELETE_ROLE = "DELETE_ROLE"; public static final String SET_PERMISSION = "SET_PERMISSION"; public static final String SET_ROLE = "SET_ROLE"; public static final String UPDATE_USER = "UPDATE_USER"; public static final String UPDATE_ME = "UPDATE_ME"; public static final String UPDATE_PASSWORD = "UPDATE_PASSWORD"; public static final String RESET_PASSWORD = "RESET_PASSWORD"; public static final String ADD_MENU = "ADD_MENU"; public static final String UPDATE_MENU = "UPDATE_MENU"; public static final String DELETE_MENU = "DELETE_MENU"; public static final String SET_MENU_ROLE = "SET_MENU_ROLE"; public static final String ADD_BLACK_IP = "ADD_BLACK_IP"; public static final String DELETE_BLACK_IP = "DELETE_BLACK_IP"; public static final String FILE_UPLOAD = "FILE_UPLOAD"; public static final String FILE_DELETE = "FILE_DELETE"; public static final String ADD_MAIL = "ADD_MAIL"; public static final String UPDATE_MAIL = "UPDATE_MAIL"; public static final String DELETE_USER = "DELETE_USER"; static { MODULES.put(LOGIN, "登陆"); MODULES.put(LOGOUT, "退出"); MODULES.put(ADD_PERMISSION, "添加权限"); MODULES.put(UPDATE_PERMISSION, "修改权限"); MODULES.put(DELETE_PERMISSION, "删除权限"); MODULES.put(ADD_ROLE, "添加角色"); MODULES.put(UPDATE_ROLE, "修改角色"); MODULES.put(DELETE_ROLE, "删除角色"); MODULES.put(SET_PERMISSION, "分配权限"); MODULES.put(SET_ROLE, "分配角色"); MODULES.put(UPDATE_USER, "修改用户"); MODULES.put(UPDATE_ME, "修改个人信息"); MODULES.put(UPDATE_PASSWORD, "修改密码"); MODULES.put(RESET_PASSWORD, "重置密码"); MODULES.put(ADD_MENU, "添加菜单"); MODULES.put(UPDATE_MENU, "修改菜单"); MODULES.put(DELETE_MENU, "删除菜单"); MODULES.put(SET_MENU_ROLE, "分配菜单"); MODULES.put(ADD_BLACK_IP, "添加黑名单"); MODULES.put(DELETE_BLACK_IP, "删除黑名单"); MODULES.put(FILE_UPLOAD, "文件上传"); MODULES.put(FILE_DELETE, "文件删除"); MODULES.put(ADD_MAIL, "保存邮件"); MODULES.put(UPDATE_MAIL, "修改邮件"); MODULES.put(DELETE_USER, "删除用户"); } }
然后我们定义一个AOP的实现类
@Aspect public class LogAop { private static final Logger logger = LoggerFactory.getLogger(LogAop.class); //对@LogAnnotation标签进行环绕增强 @Around(value = "@annotation(com.cloud.model.log.LogAnnotation)") public Object logSave(ProceedingJoinPoint joinPoint) throws Throwable { //ProceedingJoinPoint joinPoint连接点,可以通过该对象获取方法的任务信息,例如,方 Log log = new Log(); //法名,参数等。 log.setCreateTime(new Date()); LoginAppUser loginAppUser = AppUserUtil.getLoginAppUser(); if (loginAppUser != null) { log.setUsername(loginAppUser.getUsername()); } //通过连接点获取方法签名 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); //通过方法的签名获取方法的标签 LogAnnotation logAnnotation = methodSignature.getMethod().getDeclaredAnnotation(LogAnnotation.class); log.setModule(logAnnotation.module()); //我们是否要收集方法的参数,默认为true if (logAnnotation.recordParam()) { String[] paramNames = methodSignature.getParameterNames();// 获取方法参数名 if (paramNames != null && paramNames.length > 0) { Object[] args = joinPoint.getArgs();// 通过连接点获取传入参数值 //定义一个HashMap,并将对应的参数名和参数值全部放入该HashMap Map<String, Object> params = new HashMap<>(); for (int i = 0; i < paramNames.length; i++) { params.put(paramNames[i], args[i]); } try { log.setParams(JSONObject.toJSONString(params)); //Json序列化对应参数信息放入log对象中 } catch (Exception e) { logger.error("记录参数失败:{}", e.getMessage()); } } } try { Object object = joinPoint.proceed();// 执行原方法 //执行成功处理 log.setFlag(Boolean.TRUE); return object; } catch (Exception e) { //执行失败处理 log.setFlag(Boolean.FALSE); log.setRemark(e.getMessage()); throw e; } finally { // 异步将Log对象发送到队列 CompletableFuture.runAsync(() -> { try { /////////此处可以将日志对象log发送到消息队列,由于在一些系统中,一些用户的操作大量且频繁,推荐使用Kafka来作为推送日志的消息队列,比如 /////////商品购买,退货等等,在LogModule中添加对应的操作标志。 logger.info("发送日志到队列:{}", log); } catch (Exception e2) { e2.printStackTrace(); } }); } } }
因为@Aspect标签是未被Spring托管的,所以要对LogAop实行自动配置,在资源文件夹下的spring.factories中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.cloud.log.autoconfigure.LogAop
有多条以, \分隔成多行。在一些自己开发的jar包导入新的Spring boot工程中,此种方法是最为有效的依赖注入方式。
比如在用户模块要修改个人信息的时候
/** * 修改自己的个人信息 * * @param appUser * @return */ @LogAnnotation(module = LogModule.UPDATE_ME) @PutMapping("/users/me") public Map<String, Object> updateMe(@RequestBody AppUser appUser) { AppUser user = AppUserUtil.getLoginAppUser(); appUser.setId(user.getId()); appUserService.updateAppUser(appUser); return ResponseUtils.getDataResult(user); }
这个updateMe方法就会被AOP环绕增强,将操作的信息,传递参数放入log对象,并通过MQ发送到日志中心进行接收。其实这里面接入点的具体实现也是通过反射来处理的。而AOP的整个实现也是通过动态代理来实现的。当然不要忘了在本模块内的配置文件中增加
spring: aop: proxy-target-class: true
它的默认值是false,默认只能代理接口(使用JDK动态代理),当为true时,才能代理目标类(使用CGLib动态代理)。
现在我们就来自己实现一套AOP.代码延续于 @Compenent,@Autowired,@PostConstruct自实现
package com.guanjian.annotion; import java.lang.annotation.*; /** * 切面注解 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { //要拦截标签的类实例 Class<? extends Annotation> value(); }
package com.guanjian.proxy; /** * 代理接口 */ public interface Proxy { /** * 执行链式代理 * @param proxyChain * @return * @throws Throwable */ Object doProxy(ProxyChain proxyChain) throws Throwable; }
package com.guanjian.proxy; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 代理链 */ public class ProxyChain { //目标Class实例 private final Class<?> targetClass; //目标对象 private final Object targetObject; //目标方法 private final Method targetMethod; //方法代理 private final MethodProxy methodProxy; //方法参数 private final Object[] methodParams; //代理列表,因为同一个目标对象实例可能会有多个AOP的拦截,需要多个代理类实例 private List<Proxy> proxyList = new ArrayList<>(); //代理索引 private int proxyIndex = 0; public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) { this.targetClass = targetClass; this.targetObject = targetObject; this.targetMethod = targetMethod; this.methodProxy = methodProxy; this.methodParams = methodParams; this.proxyList = proxyList; } public Class<?> getTargetClass() { return targetClass; } public Method getTargetMethod() { return targetMethod; } public Object[] getMethodParams() { return methodParams; } /** * 双向调用的递归,逐个代理对象先把所有的before方法执行一遍,执行到列表中没有代理对象时执行被代理的对象的本方法 * 再逐个代理对象把所有的after方法执行一遍 * @return * @throws Throwable */ public Object doProxyChain() throws Throwable { Object methodResult; if (proxyIndex < proxyList.size()) { methodResult = proxyList.get(proxyIndex++).doProxy(this); }else { methodResult = methodProxy.invokeSuper(targetObject,methodParams); } return methodResult; } }
package com.guanjian.proxy; import java.lang.reflect.Method; /** * 切面代理 */ public abstract class AspectProxy implements Proxy { @Override public Object doProxy(ProxyChain proxyChain) throws Throwable { Object result = null; Class<?> cls = proxyChain.getTargetClass(); Method method = proxyChain.getTargetMethod(); Object[] params = proxyChain.getMethodParams(); begin(); try { if (intercept(cls,method,params)) { before(cls,method,params); //跟代理链双向递归 result = proxyChain.doProxyChain(); after(cls,method,params,result); }else { result = proxyChain.doProxyChain(); } } catch (Exception e) { System.out.println("proxy failure" + e); error(cls,method,params,e); throw e; } finally { end(); } return result; } public void begin() { } public boolean intercept(Class<?> cls,Method method,Object[] params) throws Throwable { return true; } public void before(Class<?> cls,Method method,Object[] params) throws Throwable { } public void after(Class<?> cls,Method method,Object[] params,Object result) throws Throwable { } public void error(Class<?> cls,Method method,Object[] params,Throwable e) { } public void end() { } }
package com.guanjian.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.List; /** * 代理管理器,真正实现对实际的目标类实例进行拦截,并初始化调用链,执行对代理对象进行 * 双向递归调用 */ public class ProxyManager { public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) { return (T) Enhancer.create(targetClass, new MethodInterceptor() { @Override public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable { return new ProxyChain(targetClass,targetObject,targetMethod,methodProxy,methodParams,proxyList).doProxyChain(); } }); } }
动态代理的类就是这些了,然后是跟整个IOC进行整合
在BeanHelper中增加
/** * 设置Bean实例 * @param cls * @param object */ public static void setBean(Class<?> cls,Object object) { BEAN_MAP.put(cls,object); }
在ClassHelper中增加
/** * 在扫描的类集合中获取指定父类(或接口)的子类(或实现类) * @param superClass * @return */ public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) { Set<Class<?>> classSet = new HashSet<>(); for (Class<?> cls:CLASS_SET) { if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) { classSet.add(cls); } } return classSet; } /** * 在扫描的类集合中获取有标记指定的标签类的所有类 * @param annotationClass * @return */ public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) { Set<Class<?>> classSet = new HashSet<>(); for (Class<?> cls:CLASS_SET) { if (cls.isAnnotationPresent(annotationClass)) { classSet.add(cls); } } return classSet; }
然后写一个方法拦截助手类
package com.guanjian.util; import com.guanjian.annotion.Aspect; import com.guanjian.proxy.AspectProxy; import com.guanjian.proxy.Proxy; import com.guanjian.proxy.ProxyManager; import java.lang.annotation.Annotation; import java.util.*; /** * 方法拦截助手类 */ public final class AopHelper { static { try { //AOP拦截与标记了该AOP拦截的所有类集合的map映射 Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap(); //目标类与代理类实例集合的map映射 Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap); //遍历目标类与代理类实例集合的map for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()) { //取出目标类 Class<?> targetClass = targetEntry.getKey(); //取出代理类集合列表 List<Proxy> proxyList = targetEntry.getValue(); //使用代理管理器对目标类进行拦截,并开始执行代理类集合列表的所有增强方法以及目标类的本方法 Object proxy = ProxyManager.createProxy(targetClass,proxyList); //将目标类与代理对象添加进全局映射关系 BeanHelper.setBean(targetClass,proxy); } System.out.println("aop Class loaded"); } catch (Exception e) { System.out.println("aop failure" + e); } } /** * 创建所有的AOP类与与之对应的目标类集合的映射 * @return * @throws Exception */ private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception { Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<>(); //获取切面代理类(抽象类)的所有实现类(子类) Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class); for (Class<?> proxyClass:proxyClassSet) { //实现类是否有@Aspect标签 if (proxyClass.isAnnotationPresent(Aspect.class)) { //获取该标签 Aspect aspect = proxyClass.getAnnotation(Aspect.class); //获取所有目标类集合 Set<Class<?>> targetClassSet = createTargetClassSet(aspect); //将代理类实例与该集合添加map映射 proxyMap.put(proxyClass,targetClassSet); } } return proxyMap; } /** * 创建目标类集合 * @param aspect * @return * @throws Exception */ private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception { Set<Class<?>> targetClassSet = new HashSet<>(); //获取要拦截的标签类实例 Class<? extends Annotation> annotation = aspect.value(); if (annotation != null && !annotation.equals(Aspect.class)) { //将扫描到的所有标记了该标签的所有类实例添加到集合中 targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)); } return targetClassSet; } /** * 创建目标类与代理类集合的map映射 * @param proxyMap * @return * @throws Exception */ private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception { //目标类与它所有代理集合的映射map Map<Class<?>,List<Proxy>> targetMap = new HashMap<>(); //遍历所有的AOP拦截与标记了该AOP拦截的所有类集合的map for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry:proxyMap.entrySet()) { //取出AOP拦截类 Class<?> proxyClass = proxyEntry.getKey(); //取出标记了该拦截类的目标类集合 Set<Class<?>> targetClassSet = proxyEntry.getValue(); //遍历所有的目标类 for (Class<?> targetClass:targetClassSet) { //实例化该AOP拦截类 Proxy proxy = (Proxy)proxyClass.newInstance(); //如果map中有该目标类 if (targetMap.containsKey(targetClass)) { //添加拦截类实例进列表 targetMap.get(targetClass).add(proxy); }else { //如果没有,创建代理类实例列表 List<Proxy> proxyList = new ArrayList<>(); //将该代理类实例添加进列表 proxyList.add(proxy); //创建新的目标类与该列表的映射 targetMap.put(targetClass,proxyList); } } } return targetMap; } }
注意以上代码都是针对整个目标类的所有方法进行拦截的,如果有需要可以改写对个别方法的拦截
此时我们就可以写测试代码了
测试标签类,因为是针对整个目标类的,所以是@Target(ElementType.TYPE)
package com.guanjian.annotion; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by Administrator on 2019-03-15. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationTest { }
编写一个AOP实现类,打印方法执行时间,该类必须放在扫描包中进行扫描。
package com.guanjian.test.aop; import com.guanjian.annotion.AnnotationTest; import com.guanjian.annotion.Aspect; import com.guanjian.proxy.AspectProxy; import java.lang.reflect.Method; /** * Created by Administrator on 2019-03-15. */ @Aspect(AnnotationTest.class) public class AopTest extends AspectProxy{ private long begin; @Override public void before(Class<?> cls, Method method, Object[] params) throws Throwable { begin = System.currentTimeMillis(); } @Override public void after(Class<?> cls, Method method, Object[] params, Object result) throws Throwable { System.out.println("time: " + (System.currentTimeMillis() - begin)); } }
package com.guanjian.test.compent; import com.guanjian.annotion.AnnotationTest; /** * Created by Administrator on 2019-03-16. */ @AnnotationTest public class Test3 { public void show() { System.out.println("你好,你好"); } }
注意,这些都是没有加之前的@Component标签的,因为这里不是Spring,所以它们都会被扫描到并被代理类实例增强执行。
最后是main方法
public class Test { public static void main(String[] args) { //扫描包 Manager.scanAndImp("com.guanjian.test"); //初始化AopHelper ClassUtil.loadClass(AopHelper.class.getName(),true); //这里其实拿到的是代理类的实例,代理类是目标类的子类 Test3 test3 = (Test3)Manager.getBean(Test3.class); test3.show(); } }
运行结果:
aop Class loaded
你好,你好
time: 11