AOP的好兄弟和好儿子

1.AOP是什么

面向切面编程,他是对面向对象的一种补充。

2.AOP有啥用

AOP 有助于我们将不同但是有必要的重复性代码重构为不同的模块。这么做的好处是,我们可以将这些重复性代码集中管理起来复用,而不是每次都要重复写一遍。

这种方法的好处是,代码将会变得更易于维护,从而将业务逻辑从杂乱的代码中脱离出来,专注于业务逻辑代码的开发。我们将这些不同的功能划分到不同的切面中。

也就是,图省事

3.术语

1.通知(Advice)
  你想要使用的功能,比如 安全,事物,日志等。

2.连接点(JoinPoint
  方法的前后,抛出异常的地方,都是连接点。

3.切入点(Pointcut)
  连接点可以来进行通知,但是不是所有的连接点都是有用的,那么就需要用切点来定义需要通知的方法,用切点来筛选连接点。

4.切面(Aspect)
通知:干啥,什么时候干
切点:在哪干
通知+切点=切面

5.引入(introduction)
  允许我们向现有的类添加新方法属性。就是把切面用到目标类中。

6.目标(target)
  引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.代理(proxy)
  通过代理才能实现AOP,详情看下面的好兄弟和好儿子。

8.织入(weaving)
  把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。

关键就是:切点定义了哪些连接点会得到通知

AOP实现原理

spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。

那么问题来了,要怎么伪装这个代理类,才能过JVM的安检呢。
有两种方法:

①好兄弟

实现和目标类相同的接口,我也实现和你一样的接口,反正上层都是接口级别的调用,这样我就伪装成了和目标类一样的类,一个爹的儿子,就是好兄弟,JVM一看,哦是海尔兄弟啊,就放你过去了,也就逃过了类型检查,到java运行期的时候,利用多态的后期绑定(所以spring采用运行时),伪装类(代理类)就变成了接口的真正实现,而他里面包裹了真实的那个目标类,最后实现具体功能的还是目标类,只不过伪装类在之前干了点事情(写日志,安全检查,事物等)。

这就好比张三想要拜访罗翔老师,去罗祥老师家的时候,罗翔老师的弟弟罗志祥就趴在阳台上问张三,
你是哪个?
张三分不清罗翔和罗志祥,回答说
我是张三。
然后罗志祥就在本子上记着,张三来活了。
然后张三进了屋子和罗志祥老师疯狂交流,想要聘为律师,罗志祥频频点头,搞的跟真的一样,答应了张三的请求,然后把张三愉悦送走。

回过头来罗志祥发现,坏啦爷不是律师,得让我哥来,然后罗志祥就跟罗翔老师说,今天张三来了想让你当律师。罗翔老师大喜,好啊,这个我拿手。

第二天就把问题解决了。问题解决后,罗志祥在本子上记下:张三的问题解决了。

这波啊,叫做JDK代理模式。

用代码演示一遍

代理接口,等会代理类和目标类都会实现这个接口

public interface UserDao {
      void save();
}

目标对象:罗老师的工作

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("他抢我的钱我打他的人,合适吗?合适的不得了");
    }
}

代理对象:罗志祥的工作

public class TransactionHandler implements InvocationHandler {

    //需要代理的目标对象
    //这里设计为可以为任意对象添加事务控制, 所以将目标对象声明为Object
    private Object target;

    //构造TransactionHandler时传入目标对象
    public TransactionHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用目标方法前的处理
        System.out.println("张三来案子了");
        //调用目标对象的方法
        Object result = method.invoke(target, args);
        //调用目标方法后的处理
        System.out.println("张三结案了");
        //放回方法调用结果
        return result;
    }

}

张三(测试类)

public class Main {

    public static void main(String[] args) {

        //新建目标对象
        Object target = new UserDaoImpl();

        //创建事务处理器
        TransactionHandler handler = new TransactionHandler(target);

        //生成代理类并使用接口对其进行引用
        UserDao userDao = (UserDao)Proxy.newProxyInstance(target.getClass().getClassLoader(),
        													target.getClass().getInterfaces(),
       														 handler();
        //针对接口进行方法调用
        userDao.save();
    }
}

在这里插入图片描述

②好儿子
用于没有实现接口,好兄弟行不通了。
这时候直接创造子类,当不了兄弟当儿子。

生成子类调用,这次用子类来做为伪装类,当然这样也能逃过JVM的强类型检查,我继承的吗,当然查不出来了,子类重写了目标类的所有方法,当然在这些重写的方法中,不仅实现了目标类的功能,还在这些功能之前,实现了一些其他的(写日志,安全检查,事物等)。

例子:
  罗志祥直接化身罗翔之子,学习律师知识,不仅记录张三的行踪,还亲自把事给人家办了。但是罗翔老师的举例子能力是final的,当了儿子也学不会。

这就是CGLIB。

代码实现

添加依赖包:

<dependency> 
<groupId>cglib</groupId> 
<artifactId>cglib</artifactId>
 <version>3.2.5</version> 
 </dependency>

1.创建普通类

public class Users{
 public void login(){} 
 }

2.创建创建CGLib代理器

class CgProxy implements MethodInterceptor {
 	public Object intercept(Object o, Method method,Object[]objects, MethodProxy methodProxy) throws Throwable {
	 	 System.out.println("输出语句1"); 
  		//参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法
   		//引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
    	Object obj= methodProxy.invokeSuper(o,objects);   
    	System.out.println("输出语句2"); 
    	return obj;
    } 
}

测试

public static void main(String[] args) { 
	//1.创建真实对象
 	Users users = new Users(); 
	//2.创建代理对象 
	Enhancer enhancer = new Enhancer(); 	
	enhancer.setSuperclass(users.getClass()); 	
	enhancer.setCallback(new CglibProxy());
	Users o = (Users) enhancer.create();//代理对象
 	o.login(); }

CGLIB原理不谈,俺不会。

总结

前一种兄弟模式,spring会使用JDK的java.lang.reflect.Proxy类,它允许Spring动态生成一个新类来实现必要的接口,织入通知,并且把对这些接口的任何调用都转发到目标类。

后一种父子模式,spring使用CGLIB库生成目标类的一个子类,在创建这个子类的时候,spring织入通知,并且把对这个子类的调用委托到目标类。

相比之下,还是兄弟模式好些,他能更好的实现松耦合,尤其在今天都高喊着面向接口编程的情况下,父子模式只是在没有实现接口的时候,也能织入通知,应当做一种例外。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值