AOP
1 AOP?
面向切面(方面)编程
,对业务逻辑各个部分进行隔离,降低耦合度(解耦),提高了程序的可重用性,同时提高了开发的效率不通过修改源代码的方式,在主干功能里面添加新的功能(即添加删除此功能完全耦合源代码)
2 底层原理
1、底层使用的是动态代理
(1)两种情况动态代理
第一种是有接口,使用JDK动态代理
创建接口实现类代理对象
第二种是没有接口,使用CGLIB动态代理
创建子类实现类代理对象
3 JDK动态代理实现
1、使用JDK动态代理,使用
Proxy类
里面方法创建代理对象(java.lang.Object——>java.lang.reflect.Proxy)(1)调用newProxyInstance方法(返回指定接口大代理类的实例,该接口将方法调用分派给指定的调用处理程序)
newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
三个参数:第一个参数:类加载器;第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口;第三个参数:实现这个接口 InvocationHandler,创建代理对象,写增强的方法
2、具体实现
(1)创建接口,定义方法
public interface UserDao { public int add(int a,int b); public String update(String id); }
(2)创建接口实现类,实现方法
public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a + b; } @Override public String update(String id) { return id; } }
(3)使用Proxy类创建接口代理对象
public class JDKProxy { public static void main(String[] args) { //创建接口的实现类 Class[] interfaces = {UserDao.class}; // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { // @Override //实现匿名内部类 // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // return null; // } // }); UserDaoImpl userDao = new UserDaoImpl(); UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int add = dao.add(1, 2); System.out.println("result:" + add); } } //创建代理对象 class UserDaoProxy implements InvocationHandler { //1 把创建的是谁的代理对象,需要传递那个谁 //有参构造传递 private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } /** * 增强逻辑 * * @param proxy 代理对象 * @param method 当前方法 * @param args 代理参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前执行......" + method.getName() + ":传递的参数..." + Arrays.toString(args)); //被增强的方法 Object res = method.invoke(obj, args); //方法之后 System.out.println("方法之后执行......" + obj); return res; } }
4 操作术语
前提:
class User{ add() update() delete() select() }
1、连接点
类里面哪些方法可以被增强,这些方法称为连接点
2、切入点
只增强User类中的add方法,这个方法就被称为切入点(实际被真正增强的方法)
3、通知(增强)
(1)实际增强的逻辑部分被称为通知(增强)
(2)通知有多种类型:
前置通知 方法之前执行
后置通知 方法之后执行
环绕通知 方法前后都执行
异常通知 方法出现异常执行
最终通知 类似于finally,不管有没有异常都会执行
4、切面
动作上的操作,把通知应用到切入点的过程
5 准备工作
1、Spring框架种一般是基于AspectJ实现AOP操作
(1)AspectJ?
AspectJ不是Spring的组成部分,独立AOP框架,一般是把AspectJ和SpringSpring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作
(1)基于xml配置文件
(2)基于注解方式实现(实际操作使用注解)
3、项目中引入AOP相关依赖
4、切入点表达式
(1)作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构
execution([权限修饰符] [返回类型] [类的全路径] [方法名称] ([参数列表]))
举例1:对com.znv.dao.BookDao类中的add方法进行增强
execution( * com.znv.dao.BookDao.add ( . . ) )
举例2:对com.znv.dao.BookDao类中的所有方法进行增强
execution( * com.znv.dao.BookDao.* ( . . ) )
举例2:对com.znv.dao包中所有类里面的方法进行增强
execution( * com.znv.dao.*.* ( . . ) )
6 基于AspectJ实现AOP操作
6.1 注解方式(大部分实际环境采用)
1、创建类,在类里面定义方法
//被增强的类 public class User { public void add() { System.out.println("add start ..."); } }
2、创建增强类(编写增强逻辑)
在增强类中,创建方法,不同方法代表不同的通知类型
//增强的类 public class UserProxy { //前置通知 public void before(){ System.out.println("before start ..."); } }
3、进行通知的配置
(1)在spring的配置文件中,开启注解扫描
<?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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.znv.spring.demo4.aopanno"></context:component-scan> </beans>
(2)使用注解创建User和UserProxy对象
@Component //创建对象
(3)在增强类上面添加注解@Aspect
//增强的类 @Component @Aspect //生成代理对象
(4)在spring配置文件中开启生成代理对象
<!--开启Aspect生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4、配置不同类型的通知
在增强类里面,作为通知的方法上面添加通知类型的注解,使用切入点表达式配置
//增强的类 @Component @Aspect //生成代理对象 public class UserProxy { //前置通知 //@Before注解表示作为前置通知 @Before(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void before() { System.out.println("before start ..."); } //返回值之后执行 有异常就不执行 @AfterReturning(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void afterReturning() { System.out.println("afterReturning start ..."); } //方法之后执行 不管有没有异常都执行 @After(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void after() { System.out.println("after start ..."); } //异常通知 @AfterThrowing(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void afterThrowing() { System.out.println("afterThrowing start ..."); } //环绕通知 @Around(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕之前 start ..."); //被增强方法执行 proceedingJoinPoint.proceed(); System.out.println("环绕之后 start ..."); } }
5、公共切入点抽取
类似于相同方法的提取,下面value值都是一样的,所以提取出来作为公共
//相同切入点抽取 @Pointcut(value = "execution(* com.znv.spring.demo4.aopanno.User.add(..))") public void pointdemo(){ } //前置通知 //@Before注解表示作为前置通知 @Before(value = "pointdemo()") public void before() { System.out.println("before start ..."); }
6、有多个增强类对同一个方法进行增强,设置增强类的优先级
在增强类上面添加注解@Order(数字类型值),数字类型的值越小,优先级越高
@Component @Aspect @Order(1) public class PersonProxy
7、完全使用注解开发
上述的注解方式还是用到了xml配置文件,在IOC中讲述了舍弃xml文件改写注解类config的方式
(1)创建配置类,不需要xml配置文件
@Configuration @ComponentScan(basePackages = {"com.znv.spring.demo4"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
6.2 基于配置文件
1、创建两个类,增强类和被增强类,创建方法
2、在spring配置文件中创建两个类的对象
<!--创建两个类的对象--> <bean id="book" class="com.znv.spring.demo4.aopxml.Book"></bean> <bean id="bookProxy" class="com.znv.spring.demo4.aopxml.BookProxy"></bean>
3、在spring配置文件中配置切入点
<?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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--创建两个类的对象--> <bean id="book" class="com.znv.spring.demo4.aopxml.Book"></bean> <bean id="bookProxy" class="com.znv.spring.demo4.aopxml.BookProxy"></bean> <!--配置Aop增强--> <aop:config> <!--切入点--> <aop:pointcut id="p" expression="execution(* com.znv.spring.demo4.aopxml.Book.buy(..))"></aop:pointcut> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增强作用在具体的方法上--> <aop:before method="before" pointcut-ref="p"></aop:before> </aop:aspect> </aop:config> </beans>