AOP(Aspect Oriented Programming),即面向切面编程。

1、OOP回顾

在介绍AOP之前先来回顾一下大家都比较熟悉的OOP(Object Oriented Programming)。OOP主要是为了实现编程的重用性、灵活性和扩展性。它的几个特征分别是继承、封装、多态和抽象。OOP重点体现在编程架构,强调的是类之间的层次关系。

2、OOP缺陷

为了更好的说明OOP的概念,我们接下来讲一个OOP的实例,重点分析OOP存在哪些缺陷,以便更好的理解AOP的相关内容。

先看如下的一张图:

上面这张图有三个类:Dog,Cat和Duck,他们都有一个方法run。按照OOP的设计理念,我们很容易就会想到抽象出一个Animal父类,同时让这三个子类继承Animal父类。这样的设计可以用如下的图示表示:

在OOP思想中,我们会使用大量的类似上图的编程方式,对类进行抽象、继承、封装和多态来实现编程的重用性、灵活性和扩展性。但是这样的编程仍然有一定的局限性,有时候,OOP并不能很好解决我们再实际开发中遇到的问题。为了说明这个问题,看下面的图示:

看到上面的图,我们暂时还不能发现有什么问题。为了大家便于理解,接下来我来给大家讲解一下上面类图的实现过程。描述如下:马戏团有一条表演的小狗,这条小狗可以跑和跳,但是它完成跑和跳两个动作之前必须是在接到驯兽师发出的命令后,同时完成跑和跳的动作之后,驯兽师会给与响应的奖励,比如一块肉。

了解了实现过程之后,我们在来看一下具体的代码。

    public class Dog {
public void run() {
System.out.println("驯兽师发出命令!")
System.out.println("小狗开始跑!");
System.out.pringln("驯兽师给与奖励");
}
public void jump() {
System.out.println("驯兽师发出命令!")
System.out.println("小狗开始跳!");
System.out.pringln("驯兽师给与奖励");
}
}
    仔细看上面的代码,我们可以看出在run方法和jump方法中,存在一些相同的内容(驯兽师发出命令和给与奖励),这些内容并不能完全进行抽象,即不能按照OOP编程思想进行处理。类似这样的情况同样会出现在我们编程中的很多地方,例如:日志记录、性能统计、安全控制、事务处理、异常处理等等。但是这样的情况该如何解决呢?这就引入了AOP编程思想。

3、AOP简介

AOP为Aspect Oriented Programming的缩写,即面向切面编程(也叫面向方面),是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

4、AOP实现实例

为了大家更好的理解AOP如何实现,接下来我们优化一下上述代码。

首先是Dog类

    public interface Animal {
public void run();
public void jump();
} public class Dog implements Animal{
public void run(){
System.out.println("小狗开始跑!");
}
public void jump(){
System.out.println("小狗开始跳!");
}
}

对比之前的代码我们可以明显看出,我们将关于驯兽师的相关内容从run和jump中进行了抽取,接下来,我们如何在程序运行中将关于驯兽师的动作加入到程序中呢?这就是我们这次用到的AOP实现的核心技术动态代理(Dynamic Proxy)。具体代码如下:

    public class MyProxy  implements InvocationHandler{
private Object targetObject;
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
command();
Object ret = method.invoke(targetObject, args);
award();
return ret;
} private void command() {
System.out.println("驯兽师发出命令!");
} private void award(){
System.out.println("驯兽师给与奖励!");
}
}
  上述代码实现完成之后,我们改如何调用呢?参考代码如下:
    public class Client {
public static void main(String[] args) {
MyProxy hander = new MyProxy();
Animal dog = (Animal)hander.createProxyInstance(new Dog());
dog.run();
dog.jump();
}
}
  执行结果如下:

关于AOP编程的实例演示就完成了,接下来重新回顾一下AOP与OOP的相关概念。

5、AOP与OOP的关系

OOP针对业务处理过程的实体(Dog、Cat、Duck)及其属性和行为(run)进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中(run或jump)的切面(command和award)进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

一、AOP案例如下:

1、创建如下项目结构

2、在com.entity包下创建User.java

 package com.entity;

 public class User {
private Integer id; // 用户ID
private String username; // 用户名
private String password; // 密码
private String email; // 电子邮件 // getter & setter
public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} @Override
public String toString() {
return "User [email=" + email + ", id=" + id + ", password=" + password
+ ", username=" + username + "]";
} }

User.java

3、在com.dao包下创建IUserDao.java

 package com.dao;

 import com.entity.User;

 public interface IUserDao {
public void save(User user);
}

IUserDao.java

4、在com.dao.impl包下创建UserDaoImpl.java

 package com.dao.impl;

 import com.dao.IUserDao;
import com.entity.User;
/**
* 用户DAO类,实现IDao接口,负责User类的持久化操作
*/
public class UserDaoImpl implements IUserDao{
/**
* 保存
*/
public void save(User user) {
// 这里并未实现完整的数据库操作,仅为说明问题
System.out.println("保存用户信息到数据库");
}
}

UserDaoImpl.java

5、在com.biz包下创建IUserBiz.java

 package com.biz;

 import com.entity.User;

 public interface IUserBiz {
public void addNewUser(User user);
}

IUserBiz.java

6、在com.biz.impl包下创建UserBizImpl.java

 package com.biz.impl;

 import com.biz.IUserBiz;
import com.dao.impl.UserDaoImpl;
import com.entity.User; /**
* 用户业务类,实现对User功能的业务管理
*/
public class UserBizImpl implements IUserBiz { // 声明接口类型的引用,和具体实现类解耦合
private UserDaoImpl dao; public void addNewUser(User user) {
//调用用户dao的方法保存用户信息
dao.save(user); } public UserDaoImpl getDao() {
return dao;
} public void setDao(UserDaoImpl dao) {
this.dao = dao;
} public UserBizImpl() {
} public UserBizImpl(UserDaoImpl dao) {
this.dao = dao;
} }

UserBizImpl.java

7、在com.aop包下创建LoggerBefore.java

 package com.aop;

 import java.lang.reflect.Method;
import java.util.Arrays; import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice; /**
* 通过MethodBeforeAdvice实现前置增强
*/
public class LoggerBefore implements MethodBeforeAdvice {
private static final Logger log = Logger.getLogger(LoggerBefore.class); public void before(Method method, Object[] arguments, Object target)
throws Throwable {
// Arrays.toString()数组内容转换为字符串
log.info("调用 " + target + "的" + method.getName() + "方法。方法传入参数:"
+ Arrays.toString(arguments));
} }

LoggerBefore.java

8、在com.aop包下创建LoggerAfterReturning.java

 package com.aop;

 import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
/**
* 通过AfterReturningAdvice实现后置增强
*/
public class LoggerAfterReturning implements AfterReturningAdvice {
private static final Logger log = Logger.getLogger(LoggerAfterReturning.class); public void afterReturning(Object returnValue, Method method,
Object[] arguments, Object target) throws Throwable {
log.info("调用 " + target + "的" + method.getName() + " 方法方法返回值为"
+ returnValue);
} }

LoggerAfterReturning.java

9、在src下创建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-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 实例化接口对象 -->
<bean id="userDao" class="com.dao.impl.UserDaoImpl"></bean> <!-- 实例化业务对象 -->
<bean id="biz" class="com.biz.impl.UserBizImpl">
<!-- 注入方式1:设置注入接口对象 -->
<!--
<property name="dao" ref="userDao"/>
-->
<!-- 注入方式2:构造注入对象 -->
<!-- type表示参数的类型,index表示参数的位置索引 -->
<constructor-arg index="0" ref="userDao" />
</bean> <!-- 由于默认走jdk代理,有时可能找不到代理,所以此处指定走cglib代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 实例化日志前置增强对象 -->
<bean id="loggerBefore" class="com.aop.LoggerBefore"></bean> <!-- 实例化日志后置增强对象 -->
<bean id="loggerAfterReturning" class="com.aop.LoggerAfterReturning"></bean> <!--
面向切面编程的配置即将某个功能动态的配置到某个流程中,而不改变后台代码
实现业务代码和日志代码是完全分离的,经过配置后,不做任何代码的修改,就在addNewUser方法前后实现了日志输出
-->
<aop:config>
<!-- 切入点的配置 -->
<aop:pointcut id="pointcut"
expression="execution(public void addNewUser(com.entity.User))" />
<!-- 将增强处理和切入点结合, -->
<aop:advisor pointcut-ref="pointcut" advice-ref="loggerBefore" />
<aop:advisor pointcut-ref="pointcut" advice-ref="loggerAfterReturning" />
</aop:config>
</beans> applicationContext.xml

applicationContext.xml

10、在src下创建log4j.properties

 # rootLogger是所有日志的根日志,修改该日志属性将对所有日志起作用
# 下面的属性配置中,所有日志的输出级别是info,输出源是console
log4j.rootLogger=info,console
# 定义输出源的输入位置是控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 定义输出日志的布局采用的类
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 定义日志输出布局
log4j.appender.console.layout.ConversionPattern=%d %p [%c]%n - %m%n

log4j.properties

11、在com.test包下创建Test.java

 package com.test;

 import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.biz.IUserBiz;
import com.entity.User; public class Test { public static void main(String[] args) {
//读取和加载xml配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取配置文件的bean的实例
IUserBiz biz = (IUserBiz) ctx.getBean("biz"); //实例化User对象
User user = new User();
user.setId(1);
user.setUsername("test");
user.setPassword("123456");
user.setEmail("test@pbdevj.com"); //添加信息
biz.addNewUser(user);
} }

Test.java

12、运行结果如下:

3、Spring的AOP详解和案例的更多相关文章

  1. Spring之AOP详解

    文章大纲 一.AOP介绍二.Spring的AOP实战三.AOP常用标签四.项目源码及参考资料下载五.参考文章   一.AOP介绍 1. 什么是AOP 在软件业,AOP为Aspect Oriented ...

  2. Spring、AOP详解

    如何配置AOP查看:Spring.Hello AOP 1.对于拦截规则@Pointcut的介绍: @Pointcut("execution (* cn.raffaello.service.. ...

  3. spring的aop详解

    一.aop术语 1.连接点joinpoint: 程序执行的某个特定位置:如类开始初始化之前.类初始化之后.类某个方法调用前.调用后等.Spring仅支持方法的连接点,即仅能在方法调用前.方法调用后以及 ...

  4. (三)Spring 之AOP 详解

    第一节:AOP 简介 AOP 简介:百度百科: 面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个 ...

  5. spring原理案例-基本项目搭建 02 spring jar包详解 spring jar包的用途

    Spring4 Jar包详解 SpringJava Spring AOP: Spring的面向切面编程,提供AOP(面向切面编程)的实现 Spring Aspects: Spring提供的对Aspec ...

  6. Spring Aop 详解二

    这是Spring Aop的第二篇,案例代码很详解,可以查看https://gitee.com/haimama/java-study/tree/master/spring-aop-demo. 阅读前,建 ...

  7. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  8. [Spring学习笔记 5 ] Spring AOP 详解1

    知识点回顾:一.IOC容器---DI依赖注入:setter注入(属性注入)/构造子注入/字段注入(注解 )/接口注入 out Spring IOC容器的使用: A.完全使用XML文件来配置容器所要管理 ...

  9. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

随机推荐

  1. netty入门篇(1)

    上一篇 nio简介  下一篇 netty中级篇(2) 一.为什么选择Netty Netty是最流行的框架之一.健壮性.功能.性能.可定制性和可扩展性在同类框架中首屈一指,因此被大规模使用,例如ROCK ...

  2. mybatis分页控件

    https://my.oschina.net/miemiedev/blog/135516

  3. (转载)html中div使用自动高度

    为什么要使用div标签 1.更多的配置项,那就意味着更灵活,当然,难度也更高: 2.可以方便的容纳其他html标签:     static定位就是不定位,出现在哪里就显示在哪里,这是默认取值,只有在你 ...

  4. 获取Java的32位MD5实现

    获取Java的32位MD5实现 public static String md5(String s) { char hexDigits[] = {'0','1','2','3','4','5','6' ...

  5. 注意Thread.interrupt()方法的真正作用并不是用来中断线程

      程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的.难以发现的错误.      在本篇文章中,我们针对这些难题之一:如何中断一个正在 ...

  6. mouseover和this的巧用

    mouseover & mouseout 的问题 在JS中,使用mouseover & mouseout会有触发多次的问题,这里Jquery有了替代的新属性 mouseover == ...

  7. 【Python】Python中对象管理与垃圾回收中两个很有意思的问题

    再Python中是利用引用计数来实现对象管理和垃圾回收的,即其他对象引用该对象时候,其引用计数加1,反之减1,当引用计数为0时候,被垃圾收集器回收. Python解释器对对象以及计数器的管理分为以下两 ...

  8. 【Python】使用super初始化超类

    初始化超类的传统方式,在子类的实例中调用超类的__init__()方法. 但是传统的方法有两个问题,比如: 问题1: class MyBaseClass: def __init__(self, val ...

  9. 动态多条件查询分页以及排序(一)--MVC与Entity Framework版url分页版

    一.前言 多条件查询分页以及排序  每个系统里都会有这个的代码 做好这块 可以大大提高开发效率  所以博主分享下自己的6个版本的 多条件查询分页以及排序 二.目前状况 不论是ado.net 还是EF ...

  10. label 不同颜色

    label  不同颜色 UILabel* noteLabel = [[UILabel alloc] init]; noteLabel.frame = CGRectMake(60, 100, 200, ...