关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了

BitCoralVortex
• 阅读 836

前言

OOP(Object Oriented Programing)面向对象编程

以对象为基本单位进行程序开发,通过对象间的彼此协同,相互协调,完成程序的构建

POP(Producer Oriented Programing)面向过程(方法,函数)编程

以过程为基本单位的程序开发,通过彼此间协同,相互调用,完成程序的构建

静态代理存在的问题

//实现相同的接口
public class UserServiceProxy implements UserService {
    //创建原始对象
    private UserServiceImpl userService = new UserServiceImpl();
    public void register(User user) {
        //实现额外功能
        System.out.println("-------------------");
        userService.register(user);

    }

    public boolean login(String name, String password) {
        System.out.println("____________________");
        return userService.login(name,password);
    }
}

每一个原始类都会手工编写一个代理类

  • 静态类文件数目过多,不利于项目管理
  • 代码可维护性差

概述

关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了

上图展示了一个被划分的典型应用,每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类似的辅助功能,例如安全和事务管理。

如果要重用通用功能的话,最常见的面向对象技术是继承或者委托。但是,如果在整个应用中都使用相同的基类,继承往往会导致一个脆弱的对象体系,而使用委托可能需要对委托对象进行复杂的调用。
关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了

切面提供了取代继承和委托的另一种可选方案,而且在很多场景下更清晰简洁,在使用面向切面编程时,我们仍然在一个地方定义通用功能,但是可以通过声明的方式定义这个功能要以何种方式在何处应用,而无需修改受影响的类,横切关注点模块化为特殊的类,这些类被称为切面(aspect)。这样做有两个好处:首先,现在每个关注点都集中在一个地方,而不是分散到多处代码;其次,服务模块更简洁,因为它们只包含主要关注点(或者核心功能)的代码,而次要关注点的代码被移转到切面中了。

AOP不可能取代 OOP,它只是OOP的有意补充

Spring中的AOP

AOP:本质上就是 Spring动态代理开发,有益于原始类的维护

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。

JDK动态代理的实现

代理创建三要素:1 原始对象 2 额外功能 3 代理对象实现相同的接口

JDK动态代理的核心是InvocationHandler接口和Proxy类。

public static void main(String[] args) {
    //创建原始对象
    UserService userService = new UserServiceImpl();
    //JDK创建动态代理
        Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
}
public interface InvocationHandler {
    //用于书写额外功能 额外功能:原始方法执行前后 抛出异常
    // 参数:Proxy 忽略掉,表示的是代理对象
    //method 额外功能所增加给的那个原始方法
    //Object[] args 原始方法的参数
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

interfaces:原始对象所实现的接口
userService.getClass().getInterfaces()

类加载器的作用:

  • 通过类加载器把对应类的字节码加载到JVM中
  • 通过类加载器创建class对象,进而创建这个类的对象

如何获得类加载器:每个类的.class文件 自动分配与之对应的ClassLoder

在动态代理创建的过程中,需要ClassLoader创建代理类的Class对象,可是动态代理类没有对应的.class文件,JVM也不会为其分配ClassLoader,但是又需要怎么办?(借用一个ClossLoader)

ClassLoader:完成代理类的创建
创建代理类的class对象,进而完成代理类的创建

//注意:类加载器是借用来的 可以随表找一个借用
// 在JDK8.0之前 内部变量访问外部变量需要加final
public class JDKProxy {
    public static void main(String[] args) {
        final UserService  userService = new UserServiceImpl();
        InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-----login-------");
                //原方法运行
                Object obj = method.invoke(userService, args);
                return obj;
            }
        };
        UserService service = (UserService) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        service.login("gewei","hello");
        service.register(new User());
    }
}

CGlib动态代理的实现

对于一些没有实现接口的方法

public class UserServiceImpl(){
    login();
    register();
}

代理类 继承你要代理的类

public clss Proxy extends UserServiceImpl(){
    login(){
         额外功能
         super.login();
    }
}
public class TestCglib {
    public static void main(final String[] args) {
        //创建原始对象
       final UserService userService = new UserService();
    /*
        通过cglib方式创建动态代理对象
        Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
        cglib同样也需要做这些:
        enhancer.setClassLoader();
        enhancer.setSuperclass();
        enhancer.setCallback(); -->MethodInterceptor(cglib包下)

     */
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            //等同于 InvocationHandler -- invoke
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("hello world");
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };
        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login();
        userServiceProxy.register();

    }

}

Spring中基于AspectJ注解的AOP编程

环境搭建


    <!-- 1.配置扫描的基包  -->
    <context:component-scan base-package="com.itheima"/>

    <bean id="Aspectj" class="cn.gewei.factory.Aspectj"></bean>
    //打开注解开发
<aop:aspectj-autoproxy/>

环绕通知

/**
 * 次要的业务:切面类
 */
@Component  //把这个类加到Spring容器中
@Aspect  //这个类是切面类
public class LogAspect {

    @Pointcut("execution(* com.itheima.service..*.*(..))")   //切面表达式,这是一个空方法,作用:创建切面表达式的
    public void pt() {
    }

    /**
     * 环绕通知
     */
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            System.out.println("前置通知");
            //调用目标方法
            result = joinPoint.proceed();
            System.out.println("后置通知");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常通知");
        } finally {
            System.out.println("最终通知");
        }
        //返回方法的返回值
        return result;
    }
}

前置,后置,异常,最终通知

/**
 * 次要的业务:切面类
 */
@Component  //把这个类加到Spring容器中
@Aspect  //这个类是切面类
public class LogAspect {

    @Pointcut("execution(* com.itheima.service..*.*(..))")   //切面表达式,这是一个空方法,作用:创建切面表达式的
    public void pt() {
    }

    @Before("pt()")
    public void before() {
        System.out.println("前置通知");
    }

    @AfterReturning("pt()")
    public void afterReturn() {
        System.out.println("后置通知");
    }

    @AfterThrowing("pt()")
    public void afterThrowing() {
        System.out.println("异常通知");
    }

    @After("pt()")
    public void after() {
        System.out.println("最终通知");
    }
}

切入点复用

@Pointcut("execution(* *(..))")
public void pointcut(){}

@Around(value = "pointcut()")

默认情况下Spring底层是JDK动态代理实现的

//将proxy-target-class设为true就是基于Cglib的开发模式
<aop:aspectj-autoproxy proxy-target-class="true"/>

小结

AOP是面向对象编程的一个强大补充。通过Spring的动态代理,我们现在可以把之前分散在应用各处的行为放入可重用的模块中。我们显示地声明在何处如何应用该行为。这有效减少了代码冗余,并让我们的类关注自身的主要功能。

AOP编程概念(Spring动态代理开发),通过代理类为原始类增加额外的功能,好处:利于原始类的维护。

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
推荐学java 推荐学java
3年前
推荐学java——Spring之AOP
tips:本文首发在公众号逆锋起笔,本文源代码在公众号回复aop即可查看。什么是AOP?AOP(AspectOrientProgramming),直译过来就是面向切面编程。AOP是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。为什么需要AOP?实际开发中我们应
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
SpringBoot学习:整合shiro自动登录功能(rememberMe记住我功能)
首先在shiro配置类中注入rememberMe管理器!复制代码(https://oscimg.oschina.net/oscnet/675f5689159acfa2c39c91f4df40a00ce0f.gif)/cookie对象;rememberMeCookie()方法是设置Cookie的生成模
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
BitCoralVortex
BitCoralVortex
Lv1
天涯倦客,山中归路,望断故园心眼。
文章
5
粉丝
0
获赞
0