一、概念
AOP(Aspect Oriented Programming),即面向切面编程。像日志,事务,安全等这些代码分散在各个业务逻辑之中,这样既会产生大量重复的代码也将这些逻辑与业务耦合起来。
AOP就是运用动态代理将
二、实现原理(JDK动态代理和Cglib动态代理)
JDK动态代理:代理对象实现目标对象的接口(目标对象一定是要有接口的,没有接口就不能实现动态代理)
Cglib动态代理: 代理对象继承目标对象。
JDK动态代理
1、目标类接口:
public interface IUser {
void save();
}
2、目标类
public class UserDao {
public void save() {
System.out.println("DB:保存用户");
}
public void delete() {
System.out.println("DB:删除用户");
}
}
3、测试类
public class AOP {
public void begin() {
System.out.println("开始事务");
}
public void close() {
System.out.println("关闭事务");
}
}
JDK动态代理
public class ProxyFactory {
//维护目标对象
private static Object target;
//维护关键点代码的类
private static AOP aop;
public static Object getProxyInstance(Object target_, AOP aop_) {
//目标对象和关键点代码的类都是通过外界传递进来
target = target_;
aop = aop_;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
aop.begin();
Object returnValue = method.invoke(target, args);
aop.close();
return returnValue;
}
}
);
}
}
Cglib动态代理:
/需要实现MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{
// 维护目标对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance(){
//1. 工具类
Enhancer en = new Enhancer();
//2. 设置父类
en.setSuperclass(target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("开始事务.....");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务.....");
return returnValue;
}
}
测试:
public static void main(String[] args) {
UserDao userDao = new UserDao();
UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();
factory.save();
}
三、AOP术语
连接点(Join point):目标对象里有哪些方法可以被增强,这些方法就是连接点。比如UserDao中的save()方法和delete()方法。
切点(Poincut):目标对象中具体被增强的方法。在上面的例子中就是UserDao的save()方法。
增强/通知(Advice):增强的方法。比如AOP的begin()方法和close()方法。
Spring AOP提供了5种Advice类型给我们:前置(被增强的方法之前)、后置(被增强的方法之后)、环绕(被增强的方法的前和后)、返回(后置之后)、异常。
切面(Aspect):由切点和增强/通知
组成
四、AOP实现方式
1、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:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--对象实例-->
<bean id="userDao" class="aa.UserDao"/>
<bean id="orderDao" class="aa.OrderDao"/>
<!--切面类-->
<bean id="aop" class="aa.AOP"/>
<!--AOP配置-->
<aop:config >
<!--定义切入表达式,拦截哪些方法-->
<aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/>
<!--指定切面类是哪个-->
<aop:aspect ref="aop">
<!--指定来拦截的时候执行切面类的哪些方法-->
<aop:before method="begin" pointcut-ref="pointCut"/>
<aop:after method="close" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
2、注解配置
在配置文件中开启注解配置方式
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aa"/>
<!-- 开启aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
常用注解:
-
@Aspect 指定一个类为切面类
-
@Pointcut("execution(* cn.itcast.e_aop_anno..(..))") 指定切入点表达式
-
@Before("pointCut_()") 前置通知: 目标方法之前执行
-
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
-
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
-
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
-
@Around("pointCut_()") 环绕通知: 环绕目标方法执行
@Component
@Aspect//指定为切面类
public class AOP {
//里面的值为切入点表达式
@Before("execution(* aa.*.*(..))")
public void begin() {
System.out.println("开始事务");
}
@After("execution(* aa.*.*(..))")
public void close() {
System.out.println("关闭事务");
}
}
每次写Before、After等,都要重写一次切入点表达式,这样就不优雅了。于是我们可以用@Pointcut这个注解,来指定切入点表达式:
@Component
@Aspect//指定为切面类
public class AOP {
// 指定切入点表达式,拦截哪个类的哪些方法
@Pointcut("execution(* aa.*.*(..))")
public void pt() {
}
@Before("pt()")
public void begin() {
System.out.println("开始事务");
}
@After("pt()")
public void close() {
System.out.println("关闭事务");
}
}