JAVA语言之spring框架 AOP核心详解
小标 2019-03-04 来源 : 阅读 963 评论 0

摘要:本文主要向大家介绍了JAVA语言之spring框架 AOP核心详解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言之spring框架 AOP核心详解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

JAVA语言之spring框架 AOP核心详解

AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。


一 AOP的基本概念


(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知


(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用


(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around


(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式


(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类


如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。


二 Spring AOP


Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。


三 基于注解的AOP配置方式


1.启用@AsjectJ支持


在applicationContext.xml中配置下面一句:


<aop:aspectj-autoproxy />
2.通知类型介绍


(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可


(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值


(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名


来访问目标方法中所抛出的异常对象


(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式


(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint


3.例子:

(1)Operator.java --> 切面类


@Componentbr/>@Aspect<br <="" a="">public class Operator {


@Pointcut("execution( com.aijava.springcode.service...*(..))")
public void pointCut(){}


@Before("pointCut()")
public void doBefore(JoinPoint joinPoint){
System.out.println("AOP Before Advice...");
}


@After("pointCut()")
public void doAfter(JoinPoint joinPoint){
System.out.println("AOP After Advice...");
}


@AfterReturning(pointcut="pointCut()",returning="returnVal")
public void afterReturn(JoinPoint joinPoint,Object returnVal){
System.out.println("AOP AfterReturning Advice:" + returnVal);
}


@AfterThrowing(pointcut="pointCut()",throwing="error")
public void afterThrowing(JoinPoint joinPoint,Throwable error){
System.out.println("AOP AfterThrowing Advice..." + error);
System.out.println("AfterThrowing...");
}


@Around("pointCut()")
public void around(ProceedingJoinPoint pjp){
System.out.println("AOP Aronud before...");
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("AOP Aronud after...");
}


}
(2)UserService.java --> 定义一些目标方法


@Service
public class UserService {


public void add(){
System.out.println("UserService add()");
}


public boolean delete(){
System.out.println("UserService delete()");
return true;
}


public void edit(){
System.out.println("UserService edit()");
int i = 5/0;
}


}
(3).applicationContext.xml


<context:component-scan base-package="com.aijava.springcode"/>


<aop:aspectj-autoproxy />
(4).Test.java


public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.add();
}
}
上面是一个比较简单的测试,基本涵盖了各种增强定义。注意:做环绕通知的时候,调用ProceedingJoinPoint的proceed()方法才会执行目标方法。


4.通知执行的优先级


进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After。


注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!同时使用也没啥意义。


如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。


5.切入点的定义和表达式


切入点表达式的定义算是整个AOP中的核心,有一套自己的规范


Spring AOP支持的切入点指示符:


(1)execution:用来匹配执行方法的连接点


A:@Pointcut("execution( com.aijava.springcode.service...*(..))")


第一个表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个表示所有类,第三个*表示所有方法,第二个..表示


方法的任意参数个数


B:@Pointcut("within(com.aijava.springcode.service.*)")


within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点


C:@Pointcut("this(com.aijava.springcode.service.UserService)")


this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService


D:@Pointcut("bean(userService)")


bean也是非常常用的,bean可以指定IOC容器中的bean的名称


后言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别:


1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。


2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用


6.基于XML形式的配置方式


开发中如果选用XML配置方式,通常就是POJO+XML来开发AOP,大同小异,无非就是在XML文件中写切入点表达式和通知类型


例子:


(1)Log.java


public class Log {
private Integer id;
//操作名称,方法名
private String operName;
//操作人
private String operator;
//操作参数
private String operParams;
//操作结果 成功/失败
private String operResult;
//结果消息
private String resultMsg;
//操作时间
private Date operTime = new Date();
setter,getter
}
(2).Logger.java


/**




日志记录器 (AOP日志通知)
*/
public class Logger {


@Resource
private LogService logService;


public Object record(ProceedingJoinPoint pjp){


Log log = new Log();
try {
log.setOperator("admin");
String mname = pjp.getSignature().getName();
log.setOperName(mname);


//方法参数,本例中是User user
Object[] args = pjp.getArgs();
log.setOperParams(Arrays.toString(args));


//执行目标方法,返回的是目标方法的返回值,本例中 void
Object obj = pjp.proceed();
if(obj != null){
log.setResultMsg(obj.toString());
}else{
log.setResultMsg(null);
}


log.setOperResult("success");
log.setOperTime(new Date());


return obj;
} catch (Throwable e) {
log.setOperResult("failure");
log.setResultMsg(e.getMessage());
} finally{
logService.saveLog(log);
}
return null;
}
}
(3).applicationContext.xml




<aop:config>
<aop:aspect id="loggerAspect" ref="logger">
<aop:around method="record" pointcut="(execution( com.aijava.distributed.ssh.service...add(..))
or execution( com.aijava.distributed.ssh.service...update(..))
or execution( com.aijava.distributed.ssh.service...delete*(..)))
and !bean(logService)"/>
</aop:aspect>
</aop:config>
注意切入点表达式,!bean(logService) 做日志通知的时候,不要给日志本身做日志,否则会造成无限循环!


如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。


有关更详细的Spring AOP知识,可以查看Spring官方文档第9章Aspect Oriented Programming with Spring


7.JDK动态代理介绍


例子:


(1)UserService.java


public interface UserService {


public void add();
}
(2)UserServiceImpl.java


public class UserServiceImpl implements UserService{
public void add() {
System.out.println("User add()...");
}


}
(3)ProxyUtils.java


public class ProxyUtils implements InvocationHandler{


private Object target;


public ProxyUtils(Object target){
this.target = target;
}


public Object getTarget() {
return target;
}


public void setTarget(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do sth before...");
method.invoke(target, args);
System.out.println("do sth after...");
return null;
}
}
(4)Test.java


public class Test {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyUtils proxyUtils = new ProxyUtils(userService);
UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),UserServiceImpl.class.getInterfaces(), proxyUtils);
proxyObject.add();
}
}
JDK动态代理核心还是一个InvocationHandler,记住这个就行了。


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程