Aop 的好处:
1.使得真实角色处理的业务更加纯粹。不再去关注一些公共的事情。
2.公告的业务由代理来完成。。实现业务分工
3.公共业务发生扩展时变得更加集中和方便。
名词及解释:
关注点:增加的某个业务,如日志,安全,事务等。
切面:关注点的模块化。(将日志,安全写成单独的类,该类称为切面)
连接点:表示一个方法的执行。
通知:在切面的某个特定的连接点上执行的动作。
目标对象:被代理的对象就是目标对象。
织入:把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象。
实现方式:
第一步 增加maven依赖
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.spring</groupId>
<artifactId>spring04_aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!--版本管理-->
<properties>
<spring.version>4.1.3.RELEASE</spring.version>
<aspectj.version>1.6.11</aspectj.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--测试包,自动生成-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring上下文包,在加载spring配置文件时用到-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--使用AspectJ方式注解需要相应的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!--使用AspectJ方式注解需要相应的包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
第二步 编写UserDao接口
UserDao.java
package org.spring04;
public interface UserDao {
void add();
void delete();
}
第三步 编写UserDaoImpl类,该类实现UserDao接口。
UserDaoImpl.java
package org.spring04;
/**
* 用户类(领域业务对象类)
* @author yxl
*
*/
public class UserDaoImpl implements UserDao {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
}
第四步 编写切面类 (公共业务类)Log。需实现下列接口。
实现接口MethodBeforeAdvice该拦截器会在调用方法前执行
实现接口 AfterReturningAdvice该拦截器会在调用方法后执行
实现接口 MethodInterceptor该拦截器会在调用方法前后都执行,实现环绕结果。
Log.java
package org.spring04;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
/**
切面类 (公共业务类)
实现接口MethodBeforeAdvice该拦截器会在调用方法前执行
实现接口 AfterReturningAdvice该拦截器会在调用方法后执行
实现接口 MethodInterceptor该拦截器会在调用方法前后都执行,实现环绕结果。
*/
public class Log implements MethodBeforeAdvice,AfterReturningAdvice,MethodInterceptor{
/**
* 前置通知
* @param method 被调用对象的方法
* @param args 被调用的方法参数
* @param target 被调用的方法的目标对象
*/
public void before(Method method, Object[] arg1, Object target) throws Throwable {
System.out.println("前置通知:"+target.getClass().getName()+" 的 "+method+"方法被调用了。");
}
/**
* 后置通知
*/
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("后置通知");
}
/**
* 环绕通知
*/
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕通知开始");
Object result=arg0.proceed();
System.out.println("环绕通知结束");
return result;
}
}
第五步 编写spring配置文件 .
beans.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
">
<bean id="userDaoImpl" class="org.spring04.UserDaoImpl"/>
<bean id="log" class="org.spring04.Log"></bean>
<aop:config>
<!-- 配置(连接点)关注点。指哪些业务方法需要增加公共的功能方法。 expression:匹配规则,,匹配要织入领域对象的范围。
第一个*代表返回值。
参数括号里面的两个.. 表示所有参数。 -->
<aop:pointcut id="pointcut" expression="execution(public * org.spring04.*.*(..))" />
<!-- 配置切面(公共函数) -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
</aop:config>
</beans>
第六步 编写测试类
Test.java
package org.spring04;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = (UserDao) app.getBean("userDaoImpl");
userDao.add();
}
}
注意: UserDao userDao = (UserDao) app.getBean(“userDaoImpl”);
强制转换的时候必须使用父类类型进行强转。否则动态代理生成的类无法转换到我们自定义的实现类。
错误写法: UserDao userDao = (UserDaoImpl) app.getBean(“userDaoImpl”);
错误提示:com.sun.proxy.$Proxy2 cannot be cast to org.spring04.UserDaoImpl
控制台结果:
环绕通知开始
前置通知:org.spring04.UserDaoImpl 的 public abstract void org.spring04.UserDao.add()方法被调用了。
增加了一个用户!
后置通知
环绕通知结束