最近在帮一个同事解决AOP异常时,对自己的AOP理解做了一个提纲总结,便于以后回忆。
1)AOP是什么
AOP,面向切面编程思想。该思想提倡将各组件间具有横切性质的公共逻辑用“切面”封装起来,通过代理以“切入”的方式调用,实现代码的分层、解耦。
2)AOP解决什么问题
AOP解决的问题是代码逻辑的解耦和分层、公共逻辑的封装和调用问题。
3)AOP如何解决问题
首先,按照OOP思想将公共逻辑用对象(切面)封装起来;
然后,构建业务组件的代理对象,供调用者使用,实现“偷梁换柱”;
最后,代理根据配置,在调用业务组件前后”智能“的执行切面逻辑;
4)AOP解决问题优势
业务代码和公共代码在封装和调用方面均实现分层,解耦;
提高了业务组件的复用度;
提高了公共逻辑的复用度
5)AOP与OOP的区别
从上面看出,AOP是先按照OOP思想来解决问题的,但是传统OOP思想不能解决逻辑分层和解耦问题,于是AOP通过代理手段解决该问题。
所以,AOP是OOP的补充,AOP=OOP+代理模式。
2. AOP应用
1)Spring AOP组件
目标对象:封装了业务逻辑的组件,例如Spring里的普通Service、Dao等Bean;
代理对象:目标对象的代理,运行时动态生成(JDK动态代理和CGLIB动态代理)
切面: 封装了公用逻辑的组件,一个切面包含三个元素:通知、切入点、引入;
通知: 封装了公共逻辑的切面方法(前置、后置、完成、环绕、异常)
引入: 封装了新增逻辑的组件;
切入点: 封装了连接点匹配规则的组件,常用的匹配规则有execution/args/within;
连接点: 目标对象的方法;
2)Spring AOP编程
一个登陆服务组件,其提供login操作;
在不改变该组件代码的前提下,加强其login操作逻辑、新增注册功能逻辑;
用户信息
package com.sample.domain;
public class User {
private String name;
private String password;
public User(){}
public User(String name,String password){
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String toString(){
return "用户名:<"+name+">密码:<"+password+">";
}
}
登陆服务组件接口
package com.sample.service;
import com.sample.domain.User;
public interface LoginAndoutService {
public String logIn(User user);
public void logOut(User user);
}
package com.sample.service.impl;
import org.springframework.stereotype.Service;
import com.sample.domain.User;
import com.sample.service.LoginAndoutService;
@Service
public class LoginAndoutServiceImpl implements LoginAndoutService {
@Override
public String logIn(User user) {
if(null == user)
throw new RuntimeException("用户不能为空");
return "用户<"+user.getName()+">登录成功,密码<"+user.getPassword()+">";
}
@Override
public void logOut(User user) {
System.out.println("用户<"+user.getName()+">退出!");
}
}
package com.sample.aop.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
import com.sample.aop.introduction.LogInAndOutStrong;
import com.sample.aop.introduction.impl.LogInAndOutStrongImpl;
import com.sample.domain.User;
@Component
@Aspect
public class LogInAndOutAspect {
@DeclareParents(value="com.sample.service.*+",
defaultImpl=LogInAndOutStrongImpl.class)
public static LogInAndOutStrong stronger;
@Before("com.sample.aop.pointcut.LogInAndOutPoint.logIn_Out_Point() && args(user)")
public void checkArgs(User user){
if(null == user)
throw new RuntimeException("用户不能为空!");
System.out.println("开始,检查参数值--->"+user);
}
@AfterReturning(pointcut="com.sample.aop.pointcut.LogInAndOutPoint.logIn_Out_Point()",
returning="rs")
public void checkResult(String rs){
if( null == rs)
rs = "方法为void类型,无返回值";
System.out.println("返回,接收返回值--->"+rs);
}
@After("com.sample.aop.pointcut.LogInAndOutPoint.logIn_Out_Point()")
public void finish(){
System.out.println("结束,方法调用完毕--->");
}
@AfterThrowing(pointcut="com.sample.aop.pointcut.LogInAndOutPoint.logIn_Out_Point()",
throwing="ex")
public void handException(Exception ex){
System.out.println("异常,处理异常--->"+ex);
}
@Around("com.sample.aop.pointcut.LogInAndOutPoint.logIn_Out_Point()")
public Object process(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕,调用方法--->"+pjp.getKind());
return pjp.proceed();
}
}
切面引入接口
package com.sample.aop.introduction;
import com.sample.domain.User;
public interface LogInAndOutStrong {
public String regist(User user);
}
package com.sample.aop.introduction.impl;
import com.sample.aop.introduction.LogInAndOutStrong;
import com.sample.domain.User;
public class LogInAndOutStrongImpl implements LogInAndOutStrong {
@Override
public String regist(User user) {
return "用户<"+user.getName()+">注册成功!";
}
}
package com.sample.aop.pointcut;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogInAndOutPoint {
@Pointcut("execution (* com.sample.service.impl.LoginAndoutServiceImpl.log*(..))")
public void logIn_Out_Point(){}
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 启用Spring AOP相关特性 -->
<aop:aspectj-autoproxy/>
<!-- 启用Spring 组件扫描识别特性 -->
<context:component-scan base-package="com.sample" />
</beans>
package com.sample;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sample.aop.introduction.LogInAndOutStrong;
import com.sample.domain.User;
import com.sample.service.LoginAndoutService;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
User user = new User("张三","123456");
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
LoginAndoutService service = (LoginAndoutService)context.getBean("loginAndoutServiceImpl");
System.out.println(service.logIn(user));
LogInAndOutStrong stronger = (LogInAndOutStrong)service;
System.out.println(stronger.regist(user));
}
}
测试结果
环绕,调用方法--->method-execution
开始,检查参数值--->用户名:<张三>密码:<123456>
返回,接收返回值--->用户<张三>登录成功,密码<123456>
结束,方法调用完毕--->
用户<张三>登录成功,密码<123456>
用户<张三>注册成功!