灵魂拷问:
AOP切面编程是什么?
自定义注解是什么?怎么实现功能?
1. 如何理解AOP切面编程?
AOP本质是一种编程思想,横向提取机制,将横切逻辑和业务代码进行分离,解耦合,不变原有业务逻辑的情况下,进行动态代理增强横切逻辑代码避免重复,实现业务增强。
例:下面将要实现的事务控制,项目中不同业务需要启用事务时,不使用动态代理进行业务增强,那么每实现一个业务,都要进行一次事务管理,那么将会冗余很多代码。
2. 自定义注解是什么?怎么实现功能?
注解的本质就是一个修饰符,一个标签而已,注解本身不会做任何事情,不会实现任何逻辑,只是存在于源文件中。
那么注解标记声明对象后是一个怎么样的执行逻辑呢?拿Spring @Transactional 事务注解来举例,当注解标注Impl类中方法后。在Spring IOC容器初始化时,会识别到注解标志,对有注解的标志的Impl实例对象,或者是类对象进行一次动态代理,完成之后再放入到IOC容器中,最终代码执行时,调用的是做过动态代理的实例对象,这样来实现对业务代码进行事务管理,逻辑增强。话不多说,下面上代码。
3. 可能用到的类型判断函数
-
类型判断: Instance of ,判断当前实例对象是否是xx类对象实例
ServiceInterfaceImpl serviceInterfaceImpl = new ServiceInterfaceImpl(); boolean flag = serviceInterfaceImpl instanceof ServiceInterface; boolean flag1 = serviceInterfaceImpl instanceof ServiceInterfaceImpl;
-
类型判断:isAssignableFrom 判断当前类对象,是否是本类,或者是实现类
Class impl = ClassLoader.getSystemClassLoader().loadClass("com.huey.annotation.ServiceInterfaceImpl"); Class inter = ClassLoader.getSystemClassLoader().loadClass("com.huey.annotation.ServiceInterface"); Class pro = ClassLoader.getSystemClassLoader().loadClass("com.huey.annotation.TransactionalProxy"); System.out.println(inter.isAssignableFrom(impl)); System.out.println(impl.isAssignableFrom(impl)); System.out.println(impl.isAssignableFrom(inter)); System.out.println(pro.isAssignableFrom(impl));
4. 动态代理返回对象
动态代理代理实例对象,返回的也是实例对象。
动态代理,代理类对象,返回的是类对象。
若是要对返回的Object对象操作,获取到的最终都是proxy代理对象
测试代码下面贴出
5. 自定义注解,完成事务代理简单示例代码
//接口类
public interface ServiceInterface {
void updateDatasource();
}
//接口实现类
public class ServiceInterfaceImpl implements ServiceInterface {
@Transcational
public void updateDatasource() {
System.out.println("第一次请求sql");
//故意执行错误程序
int a = 1 / 0;
System.out.println("第二次请求sql");
}
}
//动态代理类
public class TransactionalProxy {
public Object JDBCProxy(final Object obj) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入动态代理类");
System.out.println("开启事务");
Object result;
try {
result = method.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
System.out.println("回滚事务");
throw e;
}
System.out.println("结束事务");
return result;
}
});
}
}
//注解类
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transcational {
String value() default "";
}
//测试类
public class TranscationalTest {
private ServiceInterface serviceInterface;
@Test
public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//第一步,加载Class类对象
Class serviceClazz = Class.forName("com.huey.annotation.ServiceInterfaceImpl");
Class testClazz = Class.forName("com.huey.TranscationalTest");
//第二步实例化类对象
Object serviceBean = serviceClazz.newInstance();
Object testBean = testClazz.newInstance();
//第三步对类进行扫描,检测事务注解 这里直接对事务注解类操作(简单样例)
//获取到类中所有生声明的方法
Method[] methods = serviceClazz.getDeclaredMethods();
for (Method method : methods) {
//判断是否存在事务注解
Transcational transcational = method.getAnnotation(Transcational.class);
if (transcational != null) {
//第四步步对serviceBean实体,进行动态代理
TransactionalProxy transactionalProxy = new TransactionalProxy();
serviceBean = transactionalProxy.JDBCProxy(serviceBean);
// 测试动态代理返回值的部分代码
// Object o = transactionalProxy.JDBCProxy(serviceClazz);
// System.out.println(serviceBean);
// System.out.println(serviceBean.getClass().getSimpleName());
// System.out.println(o);
// System.out.println(o.getClass().getSimpleName());
}
}
//第五步,将serviceBean实例注入到test实例中
//获取Test类对象中声明的属性对象
Field field = testClazz.getDeclaredField("serviceInterface");
//判断,找到当前属性对象(Interface)的实现实例 (简单示例是不用写的,这里有个函数特别提出来一下)
if (field.getType().isAssignableFrom(serviceClazz)) {
//进行属性注入
field.setAccessible(true);
field.set(testBean, serviceBean);
}
//以上步骤均可理解为工程启动时,进行的默认加载配置
//第六步进行测试
//因为这里TranscationalTest是没有进行管理的,所以对它的实例对象进行一次变化
TranscationalTest transcationalTest = (TranscationalTest) testBean;
transcationalTest.serviceInterface.updateDatasource();
}
}
/**
数据库操作异常输出结果
进入动态代理类
开启事务
第一次请求sql
回滚事务
Caused by: java.lang.ArithmeticException: / by zero
at com.huey.annotation.ServiceInterfaceImpl.updateDatasource(ServiceInterfaceImpl.java:12)
数据库正常操作输出结果
进入动态代理类
开启事务
第一次请求sql
第二次请求sql
结束事务
Process finished with exit code 0
*/
总结:
以上则是整个自定义注解实现事务代理的一个过程。需要注意的是注解本身是不实现任何功能的,只是做一个标记而已,功能的实现都是在初始化的时候,通过对标签注解的判断,通过对实例对象,或者类对象的一个动态代理进行实现。
AOP切面编程就是一个编程思想,借助动态代理进行业务逻辑,横切逻辑的一个分离,达到解耦的目的,对业务进行增强。