Java绝地求生—Spring AOP面向切面编程

背景

在多个模块的程序中添加某功能,例如log,若直接插入log代码会造成大量重复。且在删除改功能时需要将每一处调用删除。AOP(Aspect Oriented Programming)可通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序添加功能。

动态代理

在创建代理对象时,通过构造器塞入一个目标对象(被代理对象),然后在代理对象的方法内部调用目标对象同名方法,并在调用新功能的代码。此时,代理对象 = 增强代码 + 目标对象(原对象)。动态代理通过反射实现。

构建被代理对象

创建以下文件:

  1. UserService接口
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
  1. 实现类UserServiceImpl(真实角色)
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void query() {
        System.out.println("查询用户");
    }
}

自动生成代理

创建ProxyInvocationHandler类,实现InvocationHandler接口,该类是代理实例所关联的调用处理程序类,可自动生成代理实例(代理角色)。

public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 生成得到代理
    public Object getProxy() {
    	// 参数
        // 1.用当前类的类加载器,将生成的代理类加载到JVM中
        // 2.被代理类的所有接口信息 
        // 3.动态代理方法在执行时,会调用当前类的invoke方法
        // Proxy类提供创建动态代理类和实例的静态方法
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    // 处理代理实例(代理对象),并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	// 参数
    	// 1.代理实例(代理角色)
    	// 2.代理实例当前调用的方法
    	// 3.该方法的参数
        // System.out.println(method.getName());  // 打印调用的方法名
        System.out.println("方法执行前");
        Object result = method.invoke(target, args);
        System.out.println("方法执行后");
        return result;
    }
}

调用动态代理

创建Client

public class Client {
    public static void main(String[] args) {
        // 真实角色
        UserService userService=new UserServiceImpl();

        // 调用处理程序对象
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

        // 通过调用程序处理角色 来处理要调用的接口对象
        // 设置被代理的接口
        proxyInvocationHandler.setTarget(userService);  
        // 生成代理实例(代理角色)
        UserService proxy = (UserService) proxyInvocationHandler.getProxy();   
        // 用代理实例调用方法 调用时,实际在执行invoke中的代码 
        proxy.query(); 
    }
}

执行结果
在这里插入图片描述

Spring方法

导入aspectjweaver依赖

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

service包下复用UserService接口实现类UserServiceImpl的代码。

方式一:使用Spring的API接口

log包下创建文件
1.BeforeLog类,实现MethodBeforeAdvice接口
表示在方法执行执行log方法

public class BeforeLog implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
	    // 参数
	    // method:要执行的目标对象的方法
	    // args:方法的参数
	    // target:目标对象
        System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行");
    }
}

2.AfterLog类,实现AfterReturningAdvice接口
表示在方法执行执行log方法

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    	// 参数
	    // returnValue:方法返回值
	    // method:要执行的目标对象的方法
	    // args:方法的参数
	    // target:目标对象
        System.out.println("执行了" + method.getName() + "方法,返回结果为:" + returnValue);
    }
}

resources包下编写配置文件applicationContext.xml,将以上类的bean注册到Spring中,并配置AOP

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="userService" class="service.UserServiceImpl"/>
    <bean id="log" class="log.BeforeLog"/>
    <bean id="afterLog" class="log.AfterLog"/>

    <!--配置aop-->
    <aop:config>
        <!--切入点  execution(要执行的位置 修饰符 返回值 包名.类名/接口名.方法名(参数列表))-->
        <aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>
        <!--执行环绕增强-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

编写测试类MyTest

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 将创建的代理类向上转型为接口
        UserService userService = (UserService) context.getBean("userService");
        // 执行方法
        userService.select();
    }
}

执行结果
在这里插入图片描述

方式二:使用自定义类

diy包下创建DiyPointCut

public class DiyPoint {
    public void before() {
        System.out.println("============方法执行前============");
    }

    public void after() {
        System.out.println("============方法执行后============");
    }
}

resources包下编写配置文件applicationContext.xml,将UserServiceImplDiyPointCutbean注册到Spring中,并配置自定义切面

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="userService" class="service.UserServiceImpl"/>
    <bean id="diy" class="diy.DiyPoint"/>

    <!--配置aop-->
    <aop:config>
        <!--自定义切面 ref为引用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

复用测试类MyTest
在这里插入图片描述

方式三:使用注解

diy包下创建AnnotationPointCut

@Aspect  // 标注该类是一个切面
public class AnnotationPointCut {
	// 前置
    @Before("execution(* service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("============方法执行前============");
    }

	// 后置
    @After("execution(* service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("============方法执行后============");
    }

	// 环绕
    @Around("execution(* service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");
        // 执行方法
        Object proceed = joinPoint.proceed();
        System.out.println("环绕后");
    }
}

resources包下编写配置文件applicationContext.xml,将UserServiceImplAnnotationPointCutbean注册到Spring中,并开启注解支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
                    http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/aop
                    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="service.UserServiceImpl"/>
    <bean id="annotationPointCut" class="diy.AnnotationPointCut"/>
    
    <!--开启注解支持  JDK(默认proxy-target-class="false") Cglib(proxy-target-class="true")-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>

复用测试类MyTest
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jedi Program Editor [绝地程序编辑器] V1.0.1.4一款国人造的程序编辑器 - 特别献给Delphi程序员!中国自己的 Open Source 软件,献给delphi迷们的一个小礼物包!:-)(Jedi Program Editor V1.0.0.6) 在编程中,我发现用记事本编辑程序不太方便,因此写了这个东东,我写这个东西,是因为我自己的需要,首先,再也不必为了修改一点小错误就打开Delphi环境:如改窗体的某一个属性;另外,我需要程序框架和程序功能文档的自动生成,——每次都要自己写,我已经厌倦了(目前这一功能还没有实现,sss)。它的主要特点如下:★页式多文档打开支持。 ★多种程序语言语法高亮度显示: Delphi(Object Pascal) SQL,支持以下的SQL语法: IBM DB2 Version 5 Informix Online Server 7.22 Interbase 5.0 Microsoft Access 97 Microsoft SQL Server 6.5 Oracle 8.0.0.0 Sybase Adaptive Enterprise 12.0 Basic(Visual Basic and Script) C++ Perl PHP Java HTML TCL/tk AWK Script Python CA-Clipper MS-DOS Batch File INI Files Standard ML Foxpro Fortran Baan 4GL ADSP Resource Files 68HC11 Assembler Syntax Highlighter Javascript files Syntax Highlighter Cascading Stylesheets Syntax Highlighter Cache files Syntax Highlighter Kix Scripts Syntax Highlighter Rexx Syntax Highlighter ★多种选择模式支持, ●正常选择模式 (Alt+N) ●列选择模式 (Alt+C) ●行选择模式 (Alt+L)★支持程序语言语法高亮度显示颜色的可视化调整:★支持书签:十个书签, 使用方法同Delphi: Ctrl+Shift+数字 使用或清除书签,Ctrl+数字 跳转到指定书签位置。★支持多级Undo和Redo操作,层次数可以自己设置。★支持最近文件列表。★支持查找、替换:同Delphi: ★支持直接编辑Delphi的窗体文件(dfm)。★支持直接编辑二进制文件: 热键: Ctrl+H;而Ctrl+T从二进制编辑器左边切换到右边。★支持直接将源程序文件导出为Word文档或超文本形式。★支持代码模板和代码自动完成(Code Templates): 热键:Ctrl+J★支持自动文件保存。★支持自动文件备份,你可以选择覆盖式备份或不覆盖式备份。★支持“编辑”命令键的自定义:★支持行号显示: ★可以用指定颜色显示当前行:★支持文件拖放。★ASCII 码表工具: 热键:Ctrl+Alt+A★支持自定义工具: 现在,你可以把帮助文件等其它你所中意的程序挂在它下面。★支持在文件中查找和替换: 热键: F4★支持打印。★块缩进和撤销缩进. 块缩进: Shift+Ctrl+I; 撤销缩进: Shift+Ctrl+U★在过程之间跳转:Ctrl+PGUP; Ctrl+PGDN ,仅限于Delphi(Pascal)语言.★展开/收缩代码: F6★在展开/收缩代码间导航: 到上一个收缩点 Ctrl+[, 到下一个收缩点: Ctrl+]★键盘宏纪录支持★参数设置对话框增加文件关联页面: 现在你可以在编辑器中自由的增加删除你想要的文件关联了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值