简介

AOP(面向切面编程):是一种新的方法论,是对传统OOP(面向对象编程)的补充。

AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。

AOP的好处:

每个事物逻辑位于一个位置,代码不分散,便于维护和升级。

业务模块更简洁,只包含核心业务的代码。

横切关注点:


AOP练习

Aop关于加减乘除的练习:

定义一个算法接口,含有4个方法,加减乘除。

public interface ArithmeticCalculator(){
//加
double add(double a,double b);
//减
double sub(double a,double b);
//乘
double mul(double a,double b);
//除
double div(double a,double b);
}

Aop思想:

public void before(double a ,double b){
System.out.println(a+"and"+b);
} public void after(result){
System.out.println("结果是"+result);
} public double sub(double a, double b){
before(a,b);
double result=a+b;
after(result);
return result;
}

但是,这样做是有一些弊端存在的,比如:

代码混乱:

越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。

代码分散:

以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码,如果日志需求发生变化,必须修改所有模块。

那么,如何解决这些问题呢?

这就需要使用Spring的动态代理模式来完成了。


使用动态代理解决问题

代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。


Spring AOP

用AspectJ注解声明切面

切面:带有@Aspect注解的Java类

通知:是标注有以下5种注解的简单的Java方法

@Before:前置通知,在方法执行之前执行

@After:后置通知,在方法执行之后执行

@AfterReturning:返回通知,在方法返回结果之后执行

@AfterThrowing:异常通知,在方法抛出异常之后 @Around:环绕通知,围绕着方法执行

前置后置通知

前置通知:在方法执行之前执行的通知

前置通知使用: @Before , @After注解,并将切入点表达式的值作为注解值。

利用方法签名编写AspectJ切入点表达式

最典型的切入点表达式是根据方法的签名来匹配各种方法:


指定切面的优先级

指定切面的优先级:

在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。

切面的优先级可以通过实现Ordered接口或利用@Order注解指定。

实现Ordered接口,getOrder()方法的返回值越小,优先级越高。

若使用@Order注解,序号出现在注解中。

@Aspect
@Order(0)
public class CalculaotorValidationAspect @Aspect
@Order(1)
public class CalculatorLoggingAspect

基于XML的配置声明切面

基于XML声明切面:

当使用XML声明切面时,需要在<beans>根元素中导入aop Schema

在Bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端Bean实例。

使用<aop:pointcut>来配置切点,通知元素需要用<aop:before>等元素,method属性指定切面类中通知方法的名称。


Spring实例练习

了解了这些Spring的概念知识后,必须要有一个好的例子来理解,下面就以一个简单的小例子来举例子说明。

定义一个实体类User,实现它属性的set,get方法:

public class User {
private int id;
private String uname;
private String pwd;
private int score;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}

定义一个接口ScoreService,接口中定义一个登陆方法:

import com.jredu.aop.entity.User;

public interface ScoreService {

    /**
* 登录
* @param user
* @return
*/
User login(User user); }

定义一个类ScoreServiceImpl,实现ScoreService 接口中的方法:

import org.springframework.stereotype.Component;

import com.jredu.aop.entity.User;
import com.jredu.aop.service.ScoreService;
@Component("score")
public class ScoreServiceImpl implements ScoreService{ /**
* 登录
*/
@Override
public User login(User user) {
// TODO Auto-generated method stub
System.out.println("登录成功!");
return user;
} }

在applicationContext-aop2.xml中依赖注入user,并配置扫描,配置切面等。

<?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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描标签 -->
<context:component-scan base-package="com.jredu.aop">
</context:component-scan>
<bean
id="user" class="com.jredu.aop.entity.User"
p:id="1"
p:uname="张三"
p:pwd="123456"
p:score="0"
/>
<bean
id="springAspect" class="com.jredu.aop.aspect.SpringAspect"
/>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="execution(* com.jredu.aop.service.ScoreService.login(..))" id="pointcut"/>
<!-- 配置切面 -->
<aop:aspect ref="springAspect">
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="obj"/>
</aop:aspect>
</aop:config> </beans>

定义切面类SpringAspect,在切面中定义相关方法。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; import com.jredu.aop.entity.User; public class SpringAspect { /**
* 执行前
* @param point
*//*
public void before(JoinPoint point){
String method = point.getSignature().getName();
System.out.println("before method:"+method);
}*/ /**
* 执行后
* @param point
* @throws Throwable
*/
public User around(ProceedingJoinPoint point) throws Throwable{
String method = point.getSignature().getName();
System.out.println("before method:"+method);
//调用目标方法前
User user = (User) point.proceed();
//调用目标方法后
System.out.println("原来的用户积分:"+user.getScore());
System.out.println("after method:"+method);
user.setScore(user.getScore()+100);
return user; } /**
* 最终返回结果
* @param point
* @param obj
*/
public void afterReturning(JoinPoint point,Object obj){
String method = point.getSignature().getName();
System.out.println("afterReturning method:"+method);
User user = (User) obj;
System.out.println("现在用户的积分:"+user.getScore());
}
}

测试类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.jredu.aop.entity.User;
import com.jredu.aop.service.ScoreService; public class ScoreTest { public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-aop2.xml");
User user = (User) app.getBean("user");
ScoreService service =(ScoreService) app.getBean("score");
service.login(user);
}
}

项目完成后的项目截图:

最新文章

  1. js正则表达式校验非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
  2. SoPC/Qsys杂谈
  3. sharepoint 浏览页面导航不正确
  4. nginx 默认会把header里的参数去掉下划线
  5. Selenium WebDriver + Grid2 + RSpec之旅(三) ----入门小例子
  6. excel取值
  7. Nginx源码研究五:NGINX的配置信息管理
  8. intent的startActivityForResult()方法
  9. python之列表、字典的使用
  10. Lua中的require(转)
  11. POJ 3624 Charm Bracelet 简单01背包
  12. ChatGirl 一个基于 TensorFlow Seq2Seq 模型的聊天机器人[中文文档]
  13. 树遍历(广度优先 vs 深度优先)
  14. 如何使用单例模式返回一个安全的Connection
  15. 微软BI 之SSIS 系列 - 对于平面文件中 NULL 值处理过程中容易极易混淆的几个细节
  16. POJ2125 Destroying The Graph
  17. sqlserver修改为windows验证登陆, 程序的调整
  18. sqlserver常用调优脚本
  19. Android Studio 第一次启动配置
  20. Tesseract-OCR 训练教程(一)

热门文章

  1. Linux 路由 策略路由
  2. vue项目中使用日期获取今日,昨日,上周,下周,上个月,下个月的数据
  3. docker学习之路(安装、使用)
  4. netcore.ydal可能是东半球最好ORM框架
  5. 笔记:学习go语言的网络基础库,并尝试搭一个简易Web框架
  6. Kubernetes官方java客户端之八:fluent style
  7. python_字典(dict)
  8. Python pip install 默认路径修改。
  9. 【Linux】Linux基础命令 - 目录相关的命令 ls 、cd、du
  10. 【RAC】安装rac的时候。报错checking for oracle home incompatibilities failed