摘要:关于的这种,我感觉应该是属于编译器织入,因为是通过子类生成字节码然后进行调用。
关于的Aop:
用处:可以用在连接数据库的方面,比如每一次在连接数据库的时候都需要手动新建一个连接,然后执行数据库操作,最后再来关闭数据库的连接来避免资源的消耗。这时候有人就在想那么可不可以让系统在我们每一次执行数据库操作的时候都自动的新建一个连接然后当我们执行完数据库的连接之后再自动的关闭连接呢。
这里就需要一个数据库
Aop的原理例子(会使用到cglib动态代理):
例子:
首先新建三个注解
//定义在类上面标明该类是一个切点
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
// 前置通知注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
String value();
}
//后置通知注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
String value();
}
新建一个歌曲类
public class Music {
public void sing(String str){
System.out.println(str+"唱歌");
}
}
当然,有人唱个歌就得有人做开始前的准备,例如唱歌前的准备,于是新建一个类表示唱歌前的准备:
@MyAspect // 表明这是一个切点类
public class Player {
@Before("reflec.aop.cglibtest.Music.sing()") // 前置通知,当调用sing方法被调用的时候该方法会被在它之前调用
public void beforeSing() {
System.out.println("开始唱歌前的准备");
}
@After("reflec.aop.cglibtest.Music.sing()") // 同理,在调用sing方法之后再来调用该方法
public void afterSing() {
System.out.println("唱完之后开始评分");
}
}
那么当调用sing方法的时候我们怎样调用这两个方法呢,即怎样织入这两个方法
这里就得考虑Cglib动态代理了,cglib依赖asm包,在目标类的基础上生成一个子类,然后通过子类来实现在目标方法调用的时候实现前置或者后置通知。。关于Cglib的这种,我感觉应该是属于编译器织入,因为是通过子类生成字节码然后进行调用。
建立代理类:
这个类的作用只要是通过动态代理来实现那两个方法的执行
public class CGLIBProxy implements MethodInterceptor {
private Object target;
private ProxyUtil proxyUtil ;
public CGLIBProxy(Object target) throws ClassNotFoundException {
this.target = target;
proxyUtil =new ProxyUtil();
}
public T getProxy(){
return (T) new Enhancer().create(this.target.getClass(),this);
}
public T getProxy(Class> clazz){
return (T) new Enhancer().create(this.target.getClass(),this);
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
ProxyEntity proxyEntity =new ProxyEntity(proxy,this.target.getClass(),obj,method,args);
return proxyUtil.generateEntity(proxyEntity);
}
建立反射类:(即主要是通)
public class Reflect {
Map map ; //存入的是方法名以及其注解
Map clazzMap;
public Reflect() throws ClassNotFoundException {
map=new HashMap<>();
clazzMap =new HashMap<>();
getAnnotationClass();
}
public Map getMap() { // 这里返回的是已经全部存好的map方面ProxyUtil使用
return map;
}
@Test
public void getAnnotationClass() throws ClassNotFoundException {
String clazzName="reflec.aop.cglibtest.Player";
Class> clazz =Class.forName(clazzName,false,Thread.currentThread().getContextClassLoader()); // 这里为了省事直接动态加载了该类
if (clazz.isAnnotationPresent(MyAspect.class)) { //假设是注解类
Method[] methods =clazz.getDeclaredMethods(); //遍历方法
for (Method method :methods) {
if (method.isAnnotationPresent(Before.class)) { // 获取注解
Before before =method.getAnnotation(Before.class);
String beforeValue=before.value(); // 获取注解的值以及当前类的名字方面调用方法
map.put(method.getName()+ "-"+clazzName+"-"+"before",beforeValue.substring(0,beforeValue.length()-2));
// 存入的是方法名和注解名以及执行的顺序,这里为了省事直接就在后面写了
if (method.isAnnotationPresent(After.class)) {
After after =method.getAnnotation(After.class); /
String afterValue=after.value();
map.put(method.getName()+ "-"+clazzName+"-"+"after",afterValue.substring(0,afterValue.length()-2));
}
}
}
}
建立处理这个Cglib里面的MethodInterceptor接口中的intercept方法的具体类
public class ProxyUtil {
Reflect reflect;
public ProxyUtil() throws ClassNotFoundException {
reflect = new Reflect();
}
public void getMethod(String name) {
Map map = new HashMap<>();
}
//该方法负责代理
public Object generateEntity(ProxyEntity proxyEntity) throws Throwable {
String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
Map methodMap = reflect.getMap();
for (Map.Entry map : methodMap.entrySet()) {
if (map.getValue().equals(proxyMethodValue)) {
String[] str = mapKeyDivision(map.getKey());
if (str[2].equals("before")) {
Class> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加载该类
Method method = clazz.getDeclaredMethod(str[0]);
method.invoke(clazz.newInstance(), null); // 反射调用方法
}
}
}
return doAfter(proxyEntity,methodMap); // 处理后置通知
}
private Object doAfter(ProxyEntity proxyEntity,Map map) throws Throwable {
Object object = proxyEntity.getMethodProxy().invokeSuper(proxyEntity.getObject(), proxyEntity.getArgs()); // 调用方法
String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
for(Map.Entry aMap:map.entrySet()){
if (aMap.getValue().equals(proxyMethodValue)){
String[] str =mapKeyDivision(aMap.getKey());
if(str[2].equals("after")){
Class> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加载该类
Method method = clazz.getDeclaredMethod(str[0]);
method.invoke(clazz.newInstance(), null); // 这一步需要原始的类
}
}
}
return object;
}
//分解map里面的键,因为里面存入了方法和类名以及执行顺序
private String[] mapKeyDivision(String value) {
String[] str = new String[10];
str[0] = value.substring(0, value.indexOf("-")); //注解下面的方法
str[1] = value.substring(value.indexOf("-") + 1, value.lastIndexOf("-")); //注解所在的类
str[2]=value.substring(value.lastIndexOf("-")+1,value.length()); //是before还是after
return str;
}
最后是一个bean
public class ProxyEntity {
private final MethodProxy methodProxy;
private final Class> clazz;
private final Object object;
private final Method method;
private final Object[] args;
public Object getObject() {
return object;
}
public Object[] getArgs() {
return args;
}
public Class> getClazz() {
return clazz;
}
public Method getMethod() {
return method;
}
public ProxyEntity(MethodProxy methodProxy, Class> clazz, Object object, Method method, Object[] args) {
this.methodProxy = methodProxy;
this.clazz = clazz;
this.object = object;
this.method = method;
this.args = args;
}
public MethodProxy getMethodProxy() {
return methodProxy;
}
}
最后进行测试:
public class CglibTest {
public static void main(String args[]) throws ClassNotFoundException {
Music music = new Music();
CGLIBProxy cglibProxy = new CGLIBProxy(music);
((Music)cglibProxy.getProxy()).sing("测试的人 ");
}
}
测试结果:
开始唱歌前的准备
唱歌测试的人
唱完之后开始评分
在这个测试中并没有调用Player类里面的方法却在运行的时候自动的运行了,这个例子执行简单的模仿了下Spring的AOP,其实还有好多地方都没有顾及到
这个类的思路就是先通过反射获取到切点类,然后将用注解标注的方法名以及注解里面的值存入一个map,最后在建立一个类用来处理map
Github地址:https://github.com/Somersames...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69896.html
摘要:使用与的静态代理不同,使用的动态代理,所谓的动态代理就是说框架不会去修改字节码,而是在内存中临时为方法生成一个对象,这个对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。 AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存...
摘要:,,面向切面编程。,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。例子中,注解就是切点表达式,匹配对应的连接点,通知,指在切面的某个特定的连接点上执行的动作。,织入,将作用在的过程。因为源码都是英文写的。 之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下...
摘要:在上文中,我实现了一个很简单的和容器。比如,我们所熟悉的就是在这里将切面逻辑织入相关中的。初始化的工作算是结束了,此时处于就绪状态,等待外部程序的调用。其中动态代理只能代理实现了接口的对象,而动态代理则无此限制。 1. 背景 本文承接上文,来继续说说 IOC 和 AOP 的仿写。在上文中,我实现了一个很简单的 IOC 和 AOP 容器。上文实现的 IOC 和 AOP 功能很单一,且 I...
摘要:又是什么其实就是一种实现动态代理的技术,利用了开源包,先将代理对象类的文件加载进来,之后通过修改其字节码并且生成子类。 在实际研发中,Spring是我们经常会使用的框架,毕竟它们太火了,也因此Spring相关的知识点也是面试必问点,今天我们就大话Aop。特地在周末推文,因为该篇文章阅读起来还是比较轻松诙谐的,当然了,更主要的是周末的我也在充电学习,希望有追求的朋友们也尽量不要放过周末时...
摘要:我自己总结的学习的系统知识点以及面试问题,已经开源,目前已经。目前最新的版本中模块的组件已经被废弃掉,同时增加了用于异步响应式处理的组件。每一次请求都会产生一个新的,该仅在当前内有效。显而易见,这种模式存在很多问题。 我自己总结的Java学习的系统知识点以及面试问题,已经开源,目前已经 41k+ Star。会一直完善下去,欢迎建议和指导,同时也欢迎Star: https://githu...
阅读 3137·2021-09-01 10:30
阅读 1894·2019-08-30 15:52
阅读 1160·2019-08-29 18:40
阅读 1377·2019-08-28 18:30
阅读 2596·2019-08-23 17:19
阅读 1619·2019-08-23 16:25
阅读 2991·2019-08-23 16:18
阅读 3165·2019-08-23 13:53