一. 基于注解的AOP变成开发步骤
- 原始对象
- 额外功能
- 切入点
- 组装切面
二. 开发步骤
2.1 创建带有@Aspect的切面类
package com.baizhiedu.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
* 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
*/
/**
* @Aspect注解:
* 表示这是一个切面类:额外功能+切入点
* 作用等同于:实现MethodInterceptor接口,重写invoke方法
*/
/**
* @Aroud注解:
* 相当有之前的额外功能,around()等同于invoke()
*/
@Aspect
public class MyAspectJ {
@Around("execution(* login(..))")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("注解额外功能...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
@Aspect注解:表示这是一个切面类,切面类需要包含:切入点+额外功能
2.2 添加额外功能
之前,我们创建的额外功能,是基于MethodInterceptor接口,实现该接口,重写接口中的invoke(),在invoke方法中实现了原始方法的运行+额外功能的编写;
如今,我们需要在需要额外功能的方法上,添加@Around注解,然后方法参数:ProceddingJoinPoint,这里就相当于之前invoke方法中的MethodInvocation.
@Around("暂时这里不需要填写")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("注解额外功能...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
2.3 配置切入点
@Around("execution(* login(..))")
2.4 初始化切面对象.MyAspect
<bean class="com.baizhiedu.aspectj.MyAspectJ" id="around"></bean>
2.5 重点:告诉spring,我们是基于注解开发的
<!-- 告诉spring,我们是基于注解开发的-->
<aop:aspectj-autoproxy/>
三. 测试结果:
注解额外功能...
UserServiceImpl.login
UserServiceImpl.register
四. 完成代码:
4.1 UserService
package com.baizhiedu.aspectj;
import com.baizhiedu.proxy.User;
public interface UserService {
void login(String name, int age);
void register(User user);
}
4.2 UserServiceImpl
package com.baizhiedu.aspectj;
import com.baizhiedu.proxy.User;
/**
* 原始对象
*/
public class UserServiceImpl implements UserService {
public void login(String name, int age) {
System.out.println("UserServiceImpl.login");
}
public void register(User user) {
System.out.println("UserServiceImpl.register");
}
}
4.3 MyAspect切面类:
package com.baizhiedu.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
/**
* 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
*/
/**
* @Aspect注解:
* 表示这是一个切面类:额外功能+切入点
* 作用等同于:实现MethodInterceptor接口,重写invoke方法
*/
/**
* @Aroud注解:
* 相当有之前的额外功能,around()等同于invoke()
*/
@Aspect
public class MyAspectJ {
@Around("execution(* login(..))")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("注解额外功能...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
4.4 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:p="http://www.springframework.org/schema/p"
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">
<bean id="userService" class="com.baizhiedu.aspectj.UserServiceImpl"></bean>
<bean class="com.baizhiedu.aspectj.MyAspectJ" id="around"></bean>
<!-- 告诉spring,我们是基于注解开发的-->
<aop:aspectj-autoproxy/>
</beans>
4.5 Test测试文件
package com.baizhiedu.aspectj;
import com.baizhiedu.proxy.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext7.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("Nisy",20);
userService.register(new User());
}
}
5. 基于注解的AOP编程注意事项:
5.1 细节1:切入点复用
一个切面类中,有可能出现一个log的额外功能,一个事务等多个额外功能,此时我们怎么办?死办法就是,如下:
package com.baizhiedu.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
/**
* 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
*/
/**
* @Aspect注解:
* 表示这是一个切面类:额外功能+切入点
* 作用等同于:实现MethodInterceptor接口,重写invoke方法
*/
/**
* @Aroud注解:
* 相当有之前的额外功能,around()等同于invoke()
*/
@Aspect
public class MyAspectJ {
@Around("execution(* login(..))")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("注解额外功能...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
@Around("execution(* login(..))")
public Object arround1(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("事务...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
观察以上代码,我们发现,代码出现了冗余,该怎么办呢?怎么进行代码复用呢?
答:基于注解@JonintCut定义切入点方法.没有代码块,没有返回值
@Pointcut("execution(* login(..))")
public void pointCut(){}
package com.baizhiedu.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
/**
* 通过切面类定义额外功能,@Around注解,同时也定义了切入点,around当中的参数
*/
/**
* @Aspect注解:
* 表示这是一个切面类:额外功能+切入点
* 作用等同于:实现MethodInterceptor接口,重写invoke方法
*/
/**
* @Aroud注解:
* 相当有之前的额外功能,around()等同于invoke()
*/
@Aspect
public class MyAspectJ {
//切入点复用:在切面类中定义一个函数,上面@PointCut注解,通过这种方式,定义切入点表达式,后续更加有利于切入点复用
@Pointcut("execution(* login(..))")
public void pointCut(){}
// @Around("execution(* login(..))")
@Around(value = "pointCut()")
public Object arround(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("注解额外功能...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
@Around(value = "pointCut()")
// @Around("execution(* login(..))")
public Object arround1(ProceedingJoinPoint proceedingJoinPoint){
try {
System.out.println("事务...");
Object proceed = proceedingJoinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
结果显示:
C:\Tools\jdk1.8\bin\java.exe -javaagent:D:\Browser-DownLoad\ideaIU-2019.3.win\lib\idea_rt.jar=56822:D:\Browser-DownLoad\ideaIU-2019.3.win\bin -Dfile.encoding=UTF-8 -classpath C:\Tools\jdk1.8\jre\lib\charsets.jar;C:\Tools\jdk1.8\jre\lib\deploy.jar;C:\Tools\jdk1.8\jre\lib\ext\access-bridge-64.jar;C:\Tools\jdk1.8\jre\lib\ext\cldrdata.jar;C:\Tools\jdk1.8\jre\lib\ext\dnsns.jar;C:\Tools\jdk1.8\jre\lib\ext\jaccess.jar;C:\Tools\jdk1.8\jre\lib\ext\jfxrt.jar;C:\Tools\jdk1.8\jre\lib\ext\localedata.jar;C:\Tools\jdk1.8\jre\lib\ext\nashorn.jar;C:\Tools\jdk1.8\jre\lib\ext\sunec.jar;C:\Tools\jdk1.8\jre\lib\ext\sunjce_provider.jar;C:\Tools\jdk1.8\jre\lib\ext\sunmscapi.jar;C:\Tools\jdk1.8\jre\lib\ext\sunpkcs11.jar;C:\Tools\jdk1.8\jre\lib\ext\zipfs.jar;C:\Tools\jdk1.8\jre\lib\javaws.jar;C:\Tools\jdk1.8\jre\lib\jce.jar;C:\Tools\jdk1.8\jre\lib\jfr.jar;C:\Tools\jdk1.8\jre\lib\jfxswt.jar;C:\Tools\jdk1.8\jre\lib\jsse.jar;C:\Tools\jdk1.8\jre\lib\management-agent.jar;C:\Tools\jdk1.8\jre\lib\plugin.jar;C:\Tools\jdk1.8\jre\lib\resources.jar;C:\Tools\jdk1.8\jre\lib\rt.jar;E:\myproject\spring5.x\target\classes;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-context\5.1.4.RELEASE\spring-context-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-beans\5.1.4.RELEASE\spring-beans-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-core\5.1.4.RELEASE\spring-core-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-jcl\5.1.4.RELEASE\spring-jcl-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-expression\5.1.4.RELEASE\spring-expression-5.1.4.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-webmvc\5.1.14.RELEASE\spring-webmvc-5.1.14.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-web\5.1.14.RELEASE\spring-web-5.1.14.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\slf4j\slf4j-log4j12\1.7.25\slf4j-log4j12-1.7.25.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\WorkTools\apache-maven-3.2.3\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\WorkTools\apache-maven-3.2.3\repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\springframework\spring-aop\4.3.27.RELEASE\spring-aop-4.3.27.RELEASE.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\aspectj\aspectjrt\1.9.5\aspectjrt-1.9.5.jar;D:\WorkTools\apache-maven-3.2.3\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar com.baizhiedu.aspectj.Test
2020-06-16 12:38:00 DEBUG ClassPathXmlApplicationContext:590 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6d9c638
2020-06-16 12:38:01 DEBUG XmlBeanDefinitionReader:395 - Loaded 3 bean definitions from class path resource [applicationContext7.xml]
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'org.springframework.aop.config.internalAutoProxyCreator'
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'userService'
2020-06-16 12:38:01 DEBUG ReflectiveAspectJAdvisorFactory:250 - Found AspectJ method: public java.lang.Object com.baizhiedu.aspectj.MyAspectJ.arround(org.aspectj.lang.ProceedingJoinPoint)
2020-06-16 12:38:01 DEBUG ReflectiveAspectJAdvisorFactory:250 - Found AspectJ method: public java.lang.Object com.baizhiedu.aspectj.MyAspectJ.arround1(org.aspectj.lang.ProceedingJoinPoint)
2020-06-16 12:38:01 DEBUG AnnotationAwareAspectJAutoProxyCreator:521 - Creating implicit proxy for bean 'userService' with 0 common interceptors and 3 specific interceptors
2020-06-16 12:38:01 DEBUG JdkDynamicAopProxy:118 - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [com.baizhiedu.aspectj.UserServiceImpl@366647c2]
2020-06-16 12:38:01 DEBUG DefaultListableBeanFactory:213 - Creating shared instance of singleton bean 'around'
注解额外功能...
事务...
UserServiceImpl.login
UserServiceImpl.register
5.2 细节2:动态代理的创建方式:
我们可以在测试类中,打一个端点,看一下,我们拿到的对象,在默认情况下是基于哪一种动态代理呢?下面就验证一下:
image.png
默认情况下,AOP底层的动态代理是基于:"JDK动态代理"
5.2.1 我们可以改变AOP默认的动态代理吗?
可以的,我们在基于注解的情况下,可以在applicationContext.xml配置文件中修改:
<!-- 告诉spring,我们是基于注解开发的 proxy-target-class为true,就是把jdk换成cglib代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
5.2.2 如果我们没有基于注解开发,那怎么办呢?
//proxy-target-class="true 就是把jdk动态代理换成cglib动态代理
<aop:config proxy-target-class="true">
<!-- 配置切入点-->
<!-- <aop:pointcut id="pc" expression="execution(* *(..))"/>-->
<!-- <aop:pointcut id="pc" expression="execution(* com.baizhiedu.proxy.UserServiceImpl.login(..))"/>-->
<aop:pointcut id="pc" expression="@annotation(com.baizhiedu.proxy.Log)"/>
<!-- 配置切面 整合什么? 整合额外功能-->
<aop:advisor advice-ref="arroud" pointcut-ref="pc"/>
</aop:config>
网友评论