一、Spring框架使用Api接口-继承类实现AOP的切面编程示例
要使用Spring框架AOP,除了要导入spring框架包外,还需要导入一个织入的包org.aspectj,具体maven依赖如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.9</version>
</dependency>
注,本文最先发表于publish:September 29, 2020 -Tuesday,在后来发现此时的代码已经有问题了,依赖的包加载报错,aspectj的版本需要换一下,我这里换成1.9.9后正常。
然后我们编写几个类,一个是接口类ArticleInterface,二是这个接口的实现类Article,三是需要往这个实现类Article中切入的切面类:BeforeLog 和AfterLog 分别在方法执行起和末尾执行的方法。类的实现代码如下:
//接口类ArticleInterface
package proxy;
public interface ArticleInterface {
public void query();
public void add() ;
public void edit() ;
public void delete() ;
}
//接口的实现类Article
package proxy;
public class Article implements ArticleInterface{
public void query() {
System.out.println("获取了一篇文章");
}
public void add() {
System.out.println("添加了一篇文章");
}
public void edit() {
System.out.println("修改了一篇文章");
}
public void delete() {
System.out.println("删除了一篇文章");
}
}
//前置切面类
package proxy;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class BeforeLog implements MethodBeforeAdvice{
//Method 目标对象的方法;Object:目标对象;target:目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
//此方法内容在目标方法执行前会自动执行
System.out.println("开始执行方法:" + target.getClass().getName() + "." + method.getName());
}
}
//后置切面类
package proxy;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterLog implements AfterReturningAdvice{
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("执行方法完成:" + target.getClass().getName() + "." + method.getName() );
}
}
注意,前置切面类和后置切面类分别实现了MethodBeforeAdvice和AfterReturningAdvice接口,也正因此他们能切入方法进行执行。至于切入至哪个目标类的哪个方法执行,需要我们进入applicationContext.xml中进行aop配置。如上我们需要将这两个切面类BeforeLog和AfterLog加至Article执行。applicationContext.xml的配置及运行结果如下:
<?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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 上方导入aop约束 -->
<aop:aspectj-autoproxy />
<bean id="article" class="proxy.Article" />
<bean id="beforelog" class="proxy.BeforeLog" />
<bean id="afterlog" class="proxy.AfterLog" />
<!-- 进行aop配置 -->
<aop:config>
<!-- 表示:Article.*(..) Article类的所有方法任意参数 -->
<aop:pointcut expression="execution(* proxy.Article.*(..))" id="logcut"/>
<aop:advisor advice-ref="beforelog" pointcut-ref="logcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="logcut"/>
</aop:config>
</beans>
程序的运行结果如下:
//运行结果如下:
开始执行方法:proxy.Article.add
添加了一篇文章
执行方法完成:proxy.Article.add
二、Spring使用自定义类来实现AOP切面编程
上一篇文章 使用Sprint的API(即要添加的通知功能都实现于Spring的接口)实现了AOP切面编程,也可以使用自定义的类来实现,我们可以写一个独立的CLASS类和一些方法,然后通过在applicationContext IOC容器配置中自定义切面 ,在这个切面中自定义我们的切入点并ref相关的方法从而实现切面编程。同样我们编写一个interface Printers和Computer类,还有一个自定义的横切关注点(切面,即自定义类)。代码如下:
//interface
package aspect;
public interface Printers {
public void print();
}
//interface的实现类
package aspect;
public class Computer implements Printers{
public void print() {
System.out.println("打印机执行打印");
}
}
//自定义的横切关注点类
package aspect;
public class PrintReady {
public void ready() {
System.out.println("准备墨水和纸");
}
public void clear() {
System.out.println("整理桌面");
}
}
如上,我们编写了一个计算类,其有打印的方法,而我们想在打印机执行打印前后添加点其它功能,比如打印执行前准备墨水和纸,打印后整理桌面。注意看我们的横切关注点类PrintReady没有继承任何的Spring类或者接口。然后我们开始编辑applicationContext.xml配置,以及如下:
<?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
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<bean id="computer" class="aspect.Computer" />
<bean id="printready" class="aspect.PrintReady" />
<aop:config>
<!-- 自定义切面 -->
<aop:aspect ref="printready">
<!-- 切点 -->
<aop:pointcut expression="execution(* aspect.Computer.*(..))" id="doprint"/>
<aop:before method="ready" pointcut-ref="doprint" />
<aop:after method="clear" pointcut-ref="doprint"/>
</aop:aspect>
</aop:config>
</beans>
测试类User的代码及运行结果如下:
package com.kermit.dotest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import aspect.Printers;
public class User {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Printers computer = (Printers)context.getBean("computer");
computer.print();
}
}
运行结果如下:
//运行结果
准备墨水和纸
打印机执行打印
整理桌面
三、后来使用Api接口实现AOP切面编程的补充测试记录
1. execution缺少括号不报错但是切面方法就是不执行的坑
在测试的时候发现有一个坑,aplicationContext.xml配置文件中,如果expression里面少了一个括号,程序执行并不报错,但是就是不会执行AOP切面方法,如下请注意 代码 expression="execution(* com.kermit.aoptest.Article.*(..)"/> 中少了一个右括号,程序不报错,导致排查时一直未发现,还以为是哪里加载出现了问题。
<bean id="beforelog" class="com.kermit.aoptest.BeforeLog" />
<bean id="afterlog" class="com.kermit.aoptest.AfterLog" />
<aop:config>
<aop:pointcut id="logcut" expression="execution(* com.kermit.aoptest.Article.*(..)"/>
<aop:advisor advice-ref="beforelog" pointcut-ref="logcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="logcut" />
</aop:config>
2. 使用Spring的API接口实现AOP编程有两种方式
文章1是一种实现方式,其是将要切入的类方法都继承自 Spring的类方法 org.springframework.aop.MethodBeforeAdvice;和org.springframework.aop.AfterReturningAdvice; 还有一种方式是使用自定义类,先自己配置一个类实现方法前和方法后要切入的方法,然后加入切面中。
被切入的类内容如下:
package com.kermit.aoptest;
import org.springframework.stereotype.Component;
import java.sql.SQLOutput;
@Component
public class Article implements ArticleInterface{
@Override
public void insert() {
System.out.println("insert one record.");
}
@Override
public void update() {
System.out.println("update one record.");
}
}
自定义要切入的方法内容类定义如下:
package com.kermit.aoptest;
public class DiyLog {
public void before(){
System.out.println("do before method");
}
public void after(){
System.out.println("do after method");
}
}
然后在配置文件中使用如下配置:
<bean id="diylog" class="com.kermit.aoptest.DiyLog" />
<aop:config>
<aop:aspect ref="diylog">
<aop:pointcut id="logcut" expression="execution(* com.kermit.aoptest.Article.*(..))"/>
<aop:before method="before" pointcut-ref="logcut"/>
<aop:after method="after" pointcut-ref="logcut"/>
</aop:aspect>
</aop:config>-->
这种方法与第1种方法相比,其的局限性比较大,因为其切入的只能是一些独立的方法内容,功能受限。
四、Java各数据类型的默认值/最大值/最小值
以前保存的 Java获取数据类型及基本数据类型的默认值、最大值、最小值保存在这里。
1. Java取各数据类型的认值/最大值/最小值的程序
//Java获取变量的数据类型及基本数据类型的默认值、最大最小值
package data.type;
public class Array {
static boolean bool;
static byte by;
static char ch;
static double dv;
static float fv;
static int iv;
static long lv;
static short shv;
static String strv;
public static void main(String[] args) {
//Java中的基本数据类型
System.out.println("------------各数据类型定义------------");
byte b = 2; showType(b);
short s = 3;showType(s);
int i =10; showType(i);
long l =19; showType(l);
float f=1.3f;showType(f);
double d=0.5;showType(d);
char c ='a';showType(c);
boolean bo = true; showType(bo);
//Java 基本数据类型的默认值
System.out.println("---------各类型默认值-------------");
System.out.println("Bool :" + bool);
System.out.println("Byte :" + by);
System.out.println("Character:" + ch);
System.out.println("Double :" + dv);
System.out.println("Float :" + fv);
System.out.println("Integer :" + iv);
System.out.println("Long :" + lv);
System.out.println("Short :" + shv);
System.out.println("String :" + strv);
//Java 基本数据类型的信息
System.out.println("---------各类型信息-------------");
System.out.println("byte类型字节数:"+ Byte.BYTES + ",最小值:" + Byte.MIN_VALUE + ",最大值"+Byte.MAX_VALUE);
System.out.println("short类型字节数:"+ Short.BYTES + ",最小值:" + Short.MIN_VALUE + ",最大值"+Short.MAX_VALUE);
System.out.println("int类型字节数:"+ Integer.BYTES + ",最小值:" + Integer.MIN_VALUE + ",最大值"+Integer.MAX_VALUE);
System.out.println("long类型字节数:"+ Long.BYTES + ",最小值:" + Long.MIN_VALUE + ",最大值"+Long.MAX_VALUE);
System.out.println("float类型字节数:"+ Float.BYTES + ",最小值:" + Float.MIN_VALUE + ",最大值"+Float.MAX_VALUE);
System.out.println("double类型字节数:"+ Double.BYTES + ",最小值:" + Double.MIN_VALUE + ",最大值"+Double.MAX_VALUE);
System.out.println("char类型字节数:"+ Character.BYTES + ",最小值:" + (int)Character.MIN_VALUE + ",最大值"+ (int)Character.MAX_VALUE);
}
public static void showType(Object obj) {
System.out.println(obj.getClass().getTypeName() );
}
}
2. Java取各数据类型的认值/最大值/最小值的程序运行结果:
------------各数据类型定义------------
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
java.lang.Character
java.lang.Boolean
---------各类型默认值-------------
Bool :false
Byte :0
Character:
Double :0.0
Float :0.0
Integer :0
Long :0
Short :0
String :null
---------各类型信息-------------
byte类型字节数:1,最小值:-128,最大值127
short类型字节数:2,最小值:-32768,最大值32767
int类型字节数:4,最小值:-2147483648,最大值2147483647
long类型字节数:8,最小值:-9223372036854775808,最大值9223372036854775807
float类型字节数:4,最小值:1.4E-45,最大值3.4028235E38
double类型字节数:8,最小值:4.9E-324,最大值1.7976931348623157E308
char类型字节数:2,最小值:0,最大值65535