1. AOP
1.1 AOP简介
-
AOP Aspect Oriented Programing 面向切面编程
-
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
-
Spring中的Aop是纯Java来实现的,使用动态代理的方式增强代码
-
Spring使用动态代理的机制是判断委托类是否实现了接口,如果实现了接口则使用jdk的动态代理,如果没有实现接口则使用cglib的动态代理
-
AOP不是由Spring提出来的,是由AOP联盟定义的
1.2 AOP的专业术语
Joinpoint(连接点) :委托类中可以被增强的方法
Pointcut(切入点) :切点 ,要被增强的方法
Advice(通知/增强) :增强的代码
Target(目标对象) :委托对象
Weaving(织入) :把增强应用切点的过程
Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切点和通知的结合
1.3 Spring中的AOP实现
1.3.1 传统的Spring的AOP
一个切点和一个通知的组合
1.3.2 基于Aspectj的AOP
-
AspectJ是一个基于Java语言的面向切面的AOP框架
-
Spring2.0以后新增了对AspectJ切点表达式支持
-
@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
-
新版本Spring框架,建议使用AspectJ方式来开发AOP
1.3.3 Aspectj的切点表达式
-
语法:execution(表达式)
-
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
-
public * *(..) ---检索所有的public方法
-
execution(“* cn.uplooking.spring4.demo1.dao.*(..)”) ---只检索当前包
-
execution(“* cn.uplooking.spring4.demo1.dao..*(..)”) ---检索包及当前包的子包.
1.3.4 Aspect的增强类型
-
@Before 前置通知,相当于BeforeAdvice
@Before("execution(* com.uplooking.aop.UserDao.add*(..))")
public void beforeAdvice() {
System.out.println("前置通知....");
}
-
@AfterReturning 后置通知,相当于AfterReturningAdvice
@AfterReturning(value = "execution(* com.uplooking.aop.UserDao.add*(..))", returning = "ret")
public void afterReturningAdvice(String ret) {
System.out.println("后置通知.." + ret);
}
-
@Around 环绕通知,相当于MethodInterceptor
@Around("execution(* com.uplooking.aop.UserDao.add*(..))")
public void arounrAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前..");
pjp.proceed();
System.out.println("环绕通知后..");
}
-
@AfterThrowing抛出通知,相当于ThrowAdvice
@AfterThrowing("execution(* com.uplooking.aop.UserDao.add*(..))")
public void throwAdvice() {
System.out.println("异常通知....");
}
-
@After 最终final通知,不管是否异常,该通知都会执行
@After(value = "execution(* com.uplooking.aop.UserDao.add*(..))")
public void afterAdvice() {
System.out.println("最终通知....");
}
1.4 AOP的编程(AspectJ)
1.4.1 导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
引入约束
<?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.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="com.ma.aop"/>
<!--aop自动创建代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
1.4.2(前置通知)
1.4.2.1 要被增强的类
package com.ma.aop;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void addUser(){
System.out.println("添加用户...");
}
public void deleteUser(){
System.out.println("删除用户...");
}
public void updateUser(){
System.out.println("修改用户...");
}
public void selectUser(){
System.out.println("查找用户...");
}
}
1.4.2.2 定义切面
package com.ma.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 切面 = 切点(切点表达式)+ 通知 (方法)
*/
@Aspect
@Component
public class MyAspect {
@Before("execution(* com.ma.aop.UserDao.addUser())")
public void beforeAdvice(){
System.out.println("前置通知...");
}
}
1.4.2.3测试
import com.ma.aop.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserDao {
@Autowired
private UserDao userDao;
@Test
public void testAdd(){
userDao.addUser();
}
}
1.4.2.5结果
1.4.3(后置通知)
1.4.3.1 要被增强的方法
package com.ma.aop;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void addUser(){
System.out.println("添加用户...");
}
public String deleteUser(){
System.out.println("删除用户...");
return "已删除!";
}
public void updateUser(){
System.out.println("修改用户...");
}
public void selectUser(){
System.out.println("查找用户...");
}
}
1.4.3.2 定义切面
@AfterReturning(value = "execution(* com.ma.aop.UserDao.delete*())",returning = "ret")
public void afterReturningAdvice(String ret){
System.out.println("后置通知..."+"ret");
}
注意:可以得到返回值。
1.4.3.3 测试
@Test
public void testDelete(){
userDao.deleteUser();
}
1.4.3.4 结果
1.4.4(环绕通知)
1.4.4.1 要被增强的方法
package com.ma.aop;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void addUser(){
System.out.println("添加用户...");
}
public String deleteUser(){
System.out.println("删除用户...");
return "已删除!";
}
public void updateUser(){
System.out.println("修改用户...");
}
public void selectUser(){
System.out.println("查找用户...");
}
}
1.4.4.2 定义切面
@Around("execution(* com.ma.aop.UserDao.update*())")
public void afterReturningAdvice(){
System.out.println("环绕通知...");
}
注意:此时环绕通知可以阻止目标方法的执行,需要对目标方法进行放行。
@Around("execution(* com.ma.aop.UserDao.update*())")
public void afterReturningAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知前...");
pjp.proceed();
System.out.println("环绕通知后...");
}
1.4.4.3 测试
@Test
public void testUpdate(){
userDao.updateUser();
}
1.4.4.4 效果
1.4.5(异常通知)
1.4.5.1 要被增强的方法
package com.ma.aop;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
public void addUser(){
System.out.println("添加用户...");
}
public String deleteUser(){
System.out.println("删除用户...");
return "已删除!";
}
public void updateUser(){
System.out.println("修改用户...");
}
public void selectUser(){
int i = 10/0;
System.out.println("查找用户...");
}
}
1.4.5.2 定义切面
@AfterThrowing("execution(* com.ma.aop.UserDao.selectUser())")
public void throwAdvice(){
System.out.println("异常通知...");
}
1.4.5.3 测试
@Test
public void testSelect(){
userDao.selectUser();
}
1.4.5.4 效果
2.代理模式
不使用对象的真实操作,使用我们自己创建的代理对象来操作
2.1 静态代理
委托类和代理类共同实现同一个接口,在代理类中有委托类的对象。
1、公共接口
package com.ma.aop.agent;
public interface WindWomen {
String say();
}
2、委托类
package com.ma.aop.agent;
/**
* 委托类
*/
public class Pan implements WindWomen {
public String say() {
return "晚上十点小河边见......";
}
}
3、代理类
package com.ma.aop.agent;
public class Wang implements WindWomen {
private WindWomen windWomen;//委托对象
public Wang(WindWomen windWomen){
/**
* 在代理的构造器中持有一个委托的对象
*/
this.windWomen = windWomen;
}
public String say() {
System.out.println("她找我让我给你带个话:");
System.out.println(windWomen.say());
System.out.println("记得啊!");
return null;
}
}
4、测试类
package com.ma.aop.agent;
public class AgentTest {
public static void main(String[] args) {
Pan pan = new Pan();
Wang wang = new Wang(pan);
wang.say();
}
}
5、效果
2.1动态代理
2.2.1 基于原生的JDK的动态代理
package com.ma.aop.agent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class AgentTest {
public static void main(String[] args) {
final Pan pan = new Pan();
WindWomen windWomen = (WindWomen) Proxy.newProxyInstance(AgentTest.class.getClassLoader(), Pan.class.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("她找我让我给你带个话:");
Object object = method.invoke(pan,args);
System.out.println("记得啊!");
return object;
}
});
windWomen.say();
}
}
实现
效果
2.2.2 基于CGLIB的动态代理
因为原生的jdk的动态代理存在缺陷,代理类和委托类必须实现同一个接口
所以有个开源的动态代理框架出现了(CGLIB)
CGLIB不要求委托类必须实现接口:因为CGLIB底层是基于继承实现的