什么是AOP?
AOP就是在不修改原代码的情况下,动态的添加功能
上篇文章中 我们实现了简单的IOC,这次在其基础上实现AOP,先理清一下思路,spring的AOP是基于动态代理实现的,如果被代理类实现了接口,就采用jdk动态代理的方式,如果没有就采用cglib的方式,我们实现自己的aop也是基于这两种动态代理,首先需要添加新的注解,必须要有的是切面类的注解@Aspect,再添加@Before和@After,看目录结构
首先定义切面的注解
@Aspect,(@Before,@After)定义类似,唯一注意的就是Target里面的值。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Aspect {
String value() default "";
}
修改核心类,创建几个map用来存放对象,因为实现ioc的时候map里面存放的是类定义对象(具体可以参考上篇实现ioc),如果用代理的类定义对象去覆盖原本的对象,在get的时候如果是原型模式因为会重新new,所以就不再是代理对象,这里需要重新创建一个map存放真正的代理对象,再创建一个map存放切面类的类定义对象,因为在代理类里面我们需要去获取所有的切面类,并且获取里面所有的方法,如果方法上面的切入点的方法和本次执行的方法一致,就执行对应的@Before或者@After
//存储代理对象
private Map<String,Object> proxyFactory = new ConcurrentHashMap<>();
//存储切面对象
private Map<String,Class<?>> aspectFactory = new ConcurrentHashMap<>();
在扫描包的时候,应该判断是否有@Aspect的注解,如果有就应该将类定义对象存入切面类的map,并且将jdk动态代理类和cglib动态代理类的map设置切面类的map,为了方便在代理类里面去做判断。
//通过反射创建对象
Class<?> clazz = Class.forName(pkgCls);
//判断是否需要注入bean的注解
if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Component.class)
|| clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Aspect.class)){
String key = getKey(clazz);
if (key == null){
//将名字首字母小写 作为map中的key
key = String.valueOf(fileName.charAt(0)).toLowerCase() + fileName.substring(1);
}
if (clazz.isAnnotationPresent(Aspect.class)){
//如果这个类是一个切面类 调用setAspect方法
setAspect(clazz);
aspectFactory.put(key,clazz);
//将代理类的map设置为当前切面类的map
proxyInvocation.setAspectMap(aspectFactory);
absMethodAdvance.setAspectMap(aspectFactory);
}
beanDefinationFactory.put(key, clazz);
}
实现setAspect方法,通过传入的切面类对象,去处理value值,获取被代理类的类路径,通过反射创建对象,传入代理类,获取代理对象,存入代理对象的map,注意,这里存入的是真正的对象,而不是类定义对象。
/**
* 通过传入的对象获取切入点,切入的方法等
* @param clazz
*/
private void setAspect(Class<?> clazz) {
//找到里面所有的方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods) {
method.setAccessible(true);
//判断方法上面的注解是before还是after
String[] value = null;
if (method.isAnnotationPresent(Before.class)){
//获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型
value = method.getDeclaredAnnotation(Before.class).value().split(" ");
}else if(method.isAnnotationPresent(After.class)){
value = method.getDeclaredAnnotation(After.class).value().split(" ");
}
if (value != null){
//被代理的类名
String className = value[0];
//被代理的方法名
String methodName = value[1].substring(0,value[1].indexOf("("));
//获取参数类型
String[] paramsType = value[1].substring(value[1].indexOf("(")+1,value[1].lastIndexOf(")")).split("\\,");
try {
//创建被代理对象
Class<?> proxyed = Class.forName(className);
//获取被代理对象在map中的key 也就是value值
String key = getKey(proxyed);
if (key == null){
key = className.substring(className.lastIndexOf(".")+1).substring(0,1).toLowerCase() + className.substring(className.lastIndexOf(".")+2);
}
Object obj = null;
//判断有无接口 如果实现了接口 默认用jdk动态代理 没有 则用cglib
if (proxyed.getInterfaces().length == 0){
//没有使用接口
obj = absMethodAdvance.createObject(proxyed.newInstance());
}else{
obj = proxyInvocation.createObject(proxyed.newInstance());
}
//将代理对象放入map
proxyFactory.put(key,obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
修改getBean,在最开始判断代理对象的map有无这个对象,因为里面的key和在存放的时候和被代理类的key是相同的,所以也能获取到对应的代理对象
//先判断代理对象容器中是否有这个属性 如果有 返回代理对象
if (proxyFactory.get(beanId) != null){
return proxyFactory.get(beanId);
}
实现jdk动态代理的代理类
/**
* jdk动态代理
*/
public class ProxyInvocation implements InvocationHandler {
//被代理对象
private Object target;
//存储切面类对象的map
private Map<String, Class<?>> aspectMap;
public void setAspectMap(Map<String, Class<?>> aspectMap) {
this.aspectMap = aspectMap;
}
public Object createObject(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 代理执行时候的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
//循环map
for (Map.Entry<String,Class<?>> entry:aspectMap.entrySet()) {
Class<?> clazz = entry.getValue();
//获取类下所有的方法
Method[] methods = clazz.getDeclaredMethods();
if (methods.length == 0){
obj = method.invoke(target,args);
}else {
//循环遍历方法
for (Method method1:methods) {
//获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型
String[] value = null;
//被代理的方法名
String methodName = null;
//判断方法上面是否有@Before的注解
if (method1.isAnnotationPresent(Before.class)){
//如果有 判断方法名是否相同
value = method1.getDeclaredAnnotation(Before.class).value().split(" ");
methodName = value[1].substring(0,value[1].indexOf("("));
if (methodName.equals(method.getName())){
method1.invoke(clazz.newInstance(),null);
}
}
}
obj = method.invoke(target,args);
for (Method method1:methods) {
//获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型
String[] value = null;
//被代理的方法名
String methodName = null;
//判断方法上面是否有@After的注解
if (method1.isAnnotationPresent(After.class)){
//如果有 判断方法名是否相同
value = method1.getDeclaredAnnotation(After.class).value().split(" ");
methodName = value[1].substring(0,value[1].indexOf("("));
if (methodName.equals(method.getName())){
method1.invoke(clazz.newInstance(),null);
}
}
}
}
}
return obj;
}
}
实现cglib动态代理的代理类
/**
* cglib代理模式
*/
public class AbsMethodAdvance implements MethodInterceptor {
private Object target;
//存储切面类对象的map
private Map<String, Class<?>> aspectMap;
public void setAspectMap(Map<String, Class<?>> aspectMap) {
this.aspectMap = aspectMap;
}
public Object createObject(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object obj = null;
//循环map
for (Map.Entry<String,Class<?>> entry:aspectMap.entrySet()) {
Class<?> clazz = entry.getValue();
//获取类下所有的方法
Method[] methods = clazz.getDeclaredMethods();
if (methods.length == 0){
obj = methodProxy.invokeSuper(o, objects);
}else {
//循环遍历方法
for (Method method1:methods) {
//获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型
String[] value = null;
//被代理的方法名
String methodName = null;
//判断方法上面是否有@Before的注解
if (method1.isAnnotationPresent(Before.class)){
//如果有 判断方法名是否相同
value = method1.getDeclaredAnnotation(Before.class).value().split(" ");
methodName = value[1].substring(0,value[1].indexOf("("));
if (methodName.equals(method.getName())){
method1.invoke(clazz.newInstance(),null);
}
}
}
obj = methodProxy.invokeSuper(o, objects);
for (Method method1:methods) {
//获取里面的value值 解析 里面的值实现比较简单 只有类和方法名 还有参数类型
String[] value = null;
//被代理的方法名
String methodName = null;
//判断方法上面是否有@After的注解
if (method1.isAnnotationPresent(After.class)){
//如果有 判断方法名是否相同
value = method1.getDeclaredAnnotation(After.class).value().split(" ");
methodName = value[1].substring(0,value[1].indexOf("("));
if (methodName.equals(method.getName())){
method1.invoke(clazz.newInstance(),null);
}
}
}
}
}
return obj;
}
}
接下来就是测试,创建切面类
/**
* 切面类
*/
@Aspect
public class MyAspect {
@Before("com.study.service.impl.UserServiceImpl eat()")
public void before(){
System.out.println("before");
}
@After("com.study.service.impl.UserShow run()")
public void after(){
System.out.println("after");
}
}
创建测试类,注意一个是实现了接口,一个是没有实现接口。
@Service
public class UserServiceImpl implements UserService {
@Override
public void eat() {
System.out.println("eat");
}
}
@Service
public class UserShow {
public void run(){
System.out.println("run");
}
}
编写测试代码
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext("com.study");
System.out.println("=======测试aop=======");
System.out.println("测试jdk动态代理实现");
UserService userService = (UserService) acac.getBean("userServiceImpl");
userService.eat();
System.out.println();
System.out.println("测试cglib动态代理 实现");
UserShow userShow = acac.getBean("userShow",UserShow.class);
userShow.run();
System.out.println();
测试结果
小结:
主要流程就是获取切面类里面的切入点,然后生成代理对象,通过jdk动态代理或者cglib动态代理在方法执行的前后,去添加我们想要的Before方法或者After方法,因为这个实在上篇博客自己写的ioc上完成的,所以代码也是在之上的基础上改的,建议可以先看一下上篇博客。