一.AOP(面向切面编程)
1.AOP概念
在软件业,AOP为Aspect Oriented Programming的缩写,为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,也是函数式编程的一种衍生范性,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.底层实现原理
Spring的AOP底层用到两种代理机制
- JDK的动态代理:针对实现接口的类产生代理
- Cglib的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术,生成当前类的子类对象。
- 上一篇AOP 的文章 https://mp.csdn.net/mdeditor/89003997
3.AOP的开发中相关术语
Joinpoint(连接点)
:所谓连接点就是指那些被拦截的点,在Spring中这些点指的是方法,因为Spring只支持方法类型的连接点(可以被切入的点)Pointcut(切入点)
:是指我们要对哪些Joinpoint进行拦截(已经被切入的点)Advice(通知增强)
是指拦截到的Joinpoint之后要做的事就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)Introduction(引介)
:引介是一种特殊的通知,在不修改类的代码的前提下,Introduction可以在运行期为类动态的添加一些方法或FieldAspect(切面)
:是切入点和通知(引介)的结合Proxy(代理)
:一个类被AOP织入增强后,就产生一个结果代理类Target
:目标对象Weaving(织入)
:是指把增强应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入个类装载期织入。
二、AOP开发步骤
1.创建web工程,导入jar包
2.导入日志文件log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
# error warn info debug trace
log4j.rootLogger= info, stdout
3.创建目标对象
- 编写UserService接口
package spring.aop.service;
public interface UserService {
public void save();
public void delete();
public void update();
public void select();
}
- 编写UserServiceImpl的实现类
package spring.aop.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("保存。。。");
}
@Override
public void delete() {
System.out.println("删除。。。");
}
@Override
public void update() {
System.out.println("更新。。。");
}
@Override
public void select() {
System.out.println("查询。。。");
}
}
4.编写通知
package spring.aop.service.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class TransactionAdvice {
// 前置通知:在目标方法之前调用
// 后置通知:在目标方法之后调用(出现异常就不会调用了)
// 最终通知:在目标方法之后调用(不管发生什么都会调用)
// 环绕通知:在目标方法之前、后都会调用
// 异常通知:在目标方法出现异常时调用
public void before() {
System.out.println("前置通知执行:在目标方法之前调用");
}
public void afterReturning() {
System.out.println(" 后置通知执行:在目标方法之后调用(出现异常就不会调用了)");
}
public void after() {
System.out.println("最终通知执行:在目标方法之后调用(不管发生什么都会调用");
}
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕通知:在目标方法之前、后都会调用");
Object proceed = point.proceed();
System.out.println("环绕通知:在目标方法之前、后都会调用");
return proceed;
}
public void afterException() {
System.out.println("异常通知执行:在目标方法出现异常时调用");
}
}
5.配置织入,将通知织入到目标对象
- 编写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"
xmlns:context="http://www.springframework.org/schema/context"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--织入目标对象 -->
<bean name="userService" class="spring.aop.service.UserServiceImpl" />
<!--通知对象 -->
<bean name="transactionAdvice" class="spring.aop.service.advice.TransactionAdvice" />
<!--将通知对象织入目标对象 -->
<aop:config>
<!--选择切入点 -->
<aop:pointcut expression="execution(public void spring.aop.service.UserServiceImpl.update())" id="pointcut" />
<!-- 引入通知对象 -->
<aop:aspect ref="transactionAdvice">
<aop:before method="before" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
</beans>
6.编写测试类
package spring.aop.service;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Resource(name = "userService")
private UserService userService;
@Test
public void TestUpdate() {
userService.update();
}
@Test
public void TestSave() {
userService.save();
}
@Test
public void TestDelete() {
userService.delete();
}
}
- 其他详细配置
<?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"
xmlns:context="http://www.springframework.org/schema/context"
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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--织入目标对象 -->
<bean name="userService" class="spring.aop.service.UserServiceImpl" />
<!--通知对象 -->
<bean name="transactionAdvice" class="spring.aop.service.advice.TransactionAdvice" />
<!--将通知对象织入目标对象 -->
<aop:config>
<!--选择切入点 ,正则表达式原理-->
<aop:pointcut expression="execution(* spring.aop.service..*ServiceImpl.*(..))" id="pointcut" />
<!-- 引入通知对象 -->
<aop:aspect ref="transactionAdvice">
<aop:before method="before" pointcut-ref="pointcut" />
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" />
<aop:after method="after" pointcut-ref="pointcut" />
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterException" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
</beans>
注解开发
- 一切都不变,配置文件改变。